>> On today's Visual Studio Toolbox,
part 5 of our Entity Framework Core series.
Phil is going to put the CAD in CRAD.
[MUSIC]
>> Hi, welcome to Visual Studio Toolbox.
I'm your host, Robert Green, and joining
me is Phil Japikse. Hey, Phil.
>> Hey, Robert. How are you?
>> I'm great. This is part 5 of our Entity Framework Core series.
>> Correct. This is part 5 of the intro
to Entity Framework Core series.
The next parts will be the advanced Entity Framework Core series.
>> Okay. So this is the wrap up of our introduction.
In the previous episode, we looked at querying data.
So in this episode, we should probably look at
modifying data, adding, editing, deleting.
>> Correct.
>> All right.
>> All right. So we talked about the advantages
of the ChangeTracker in episode 2.
>> Right.
>> That it is tracking forth all of
those things that changed, get added,
deleted, modified, so that when we call
SaveChanges on the DbContext,
then it all wraps up for us, creates a SQL,
and goes and does its thing.
So let's jump into the code and actually
make some changes and process them to the database.
Now, I just have very simple methods here,
add an item, add multiple items,
and add an object graph.
But I want to explain some of the setup before we dive into it.
I have, in each of these, a local function,
which is a fairly new C# construct.
Then I have this other helper method
called ShouldExecuteInATransaction.
The reason why I'm spending time to explain
this is because if you remember from earlier episodes,
I said when you call SaveChanges on the DbContext,
it is automatically executing in a transaction.
But we can also enlist our own transaction and use
that instead of the implicit transaction that gets created.
So let's go down here to this method.
It should be very familiar code if you use transactions in ADO.NET.
What I'm saying is, here's my transaction
on to the database facade that's attached to the DbContext.
Remember, the DbContext is like
the master control program or the general contractor.
It then says to the database facade,
"Hey, here's a transaction I want you to use."
So then we can execute whatever we're doing in our code.
In this case, I'm automatically rolling it back.
This is a paradigm that I use when I'm
doing integration testing against EF Core
so that I always return the database to where it began.
It's the same thing I'm doing in this example so that
we don't mess up the data as we run through these samples.
So this is showing how you can use
your own transaction with EF Core.
Again, if I didn't do this and I just call SaveChanges,
then it would happen in its own transaction.
Does that make sense, Robert?
>> Yes.
>> Okay. So if I want to add an item,
then we should probably call SaveChanges.
So here, I create a new person objects,
and I add it into the DbSet that's person.
That's the specialized collection on
the context that holds all the person objects.
When I call SaveChanges,
that's going to happen inside a transaction by itself.
But in our case, it's actually get plugged into
the transaction that I've created so I can roll it back.
So what this does is creates a SQL statement that says,
insert into person and just these fields right here.
Now, the other thing that this does for us is it
executes any server-side things that is for example,
rowversion computed fields or computed columns,
I should say, and sequence.
So if the primary key was a sequence,
what comes back on this person object are those fields.
So to two-step process, calls into the server, does its changes,
but then it returns basically scope identity
and populate our person objects
with those server-side fields.
So as a developer,
I don't have to go back and say,
tell me what the primary key is.
So it keeps it in sync.
Now, that's a simple add.
We can also add a list of items,
and that is done by calling AddRange.
So this will take each of those items in
a transaction or all of those items in a transaction,
and then attempt to save them to the database.
>> Okay.
>> Now, if we had a unique constraint on first name or last name,
this would obviously fail.
Then what comes back is,
and we'll talk about this in a later episode and
how we handle the different exceptions coming from the database,
but this would throw a DbUpdateException
and enable us to figure out what happened as developers,
but we're keeping these episodes very straightforward.
The final thing I want to talk about is adding an object graph.
So here, I have a person object
and then adding to their e-mail address collection.
As we've said throughout this show,
we should have called that e-mail addresses,
but we're adding this e-mail address into the collection.
What I can do is just call Add on that person,
and it also knows that this e-mail address
was added to that person collection
that will build the appropriate SQL
to add all the objects all the way down,
so it add the object graph for you.
Now, to delete an item, very simply,
we're going to go back to the ChangeTracking,
and we're going to go into the DeleteEntity right here,
and we call Remove.
>> Okay.
>> Now, there's a couple different ways we can do this.
So the first one is to call Remove on an object
that is in the collection.
The other way is to set the entity state to delete it.
Remember, when we called Remove on this
and we show the entity state back in episode 2,
it showed up as deleted.
If we don't have it in the collection,
we could say context.Entry.
Actually, I want to copy it from up here.
Usually, we have these pre-baked.
But sometimes, we go off-script.
In this case, we're going off-script.
So here is another option, but I don't have it in the collection.
For example, maybe I know the primary key,
so I can create a new person object that just has the primary key.
I don't have to query the database to get it back.
I set the state to deleted,
and then I can call SaveChanges,
and it will still execute the deleted or the delete statement.
>> Then the remove,
that doesn't send this SQL back either
until you call SaveChanges, correct?
>> Right.
>> Okay.
>> Yeah. So nothing happens in the entire world
until SaveChanges is called.
>> Yeah. Okay.
>> Then to edit, we simply make some changes,
and then we can call context.Person.Update(person).
Then we still have to call SaveChanges,
and it'll update that record.
>> Right. Cool.
>> So it's really pretty simple.
The thing that you want to remember is that
SaveChanges will save everything that's in
the ChangeTracker and marked as has some change.
So if I had a customer that was changed,
and a person that was changed,
and a employee that was added,
and I call SaveChanges,
that all happens in a transaction,
and normally, that's what you want.
>> Right.
>> You can also create your own transaction.
Why would you need to create your own transaction?
Let's say you are going against two different tables
and you wanted to run a process,
the typical accounting example,
I'm going to withdraw money from
one account and deposit to another account.
But maybe they weren't in the same database,
so I have different contexts.
So I'm going to wrap each of those in
my own transaction so that I make sure that
they both work without error and then I can commit them.
>> Right.
>> The possibilities are endless of how you
can work with the transactions and everything else.
But I wanted to get the basic save.
Update is for modified.
Remove is for deleted.
I showed you we can also just set to deleted status
and then call SaveChanges and then Add.
Then there's the range version of all of these, AddRange,
UpdateRange, DeleteRange,
and that works on in IEnumerable.
>> So do you ever use setting the state to
deleted versus explicitly removing?
>> So for deleted, I almost always set the state
to deleted and do it this way.
Because the advantage is,
let me add some notes here.
This must be in memory,
this isn't in memory.
What I mean is, retrieved from database.
>> Okay. So if this is a case where you call up the record,
you look at it, and then there's a "Delete" button,
then it's in memory and you can just remove.
But if when you remove that,
you also remove data from three other tables behind the scenes,
then it's easier to set state to deleted, is that correct?
>> No. Let me back up because I want to talk
about the difference between
a web world and always connected world like WPF.
So let's take your first example.
I'm looking at a screen,
and it shows a customer record, and I want to delete it.
>> Right.
>> If I'm in a web world,
the work has been done to retrieve that record.
Now, I hit the button,
it's going to post back to the server,
and the server then,
it's got the primary key from the form collection,
it's got some other values,
but now, I have to delete it.
If I want to use the remove method,
I have to query that record from the database
to get it into the DbSet to call Remove on it.
So I'm doing an extra database call
just to turn around and delete it.
>> But if you've already retrieved it to display it,
isn't it already in memory?
>> No. Because we're in the web world,
that's why I want to make the distinction.
If I'm in the web world,
I've done a get, now I'm disconnected.
Yes, the data is displayed,
but I do not have it in the DbContext
because at the end of that request,
that DbContext gets recycled.
>> I see. Okay.
>> So now, I'm doing a POST.
So on the POST,
due to DI container within ASP on our Core,
it's creating a new instance of that DbContext
or getting it from the DbContext pool.
But either way, it's clean, there's nothing in it.
So in a web world,
I use this paradigm right here, almost exclusively.
I've got the ID, I've got the rowversion for concurrency,
which we'll talk about in later episode.
So then I say, "Okay. Make me a new person
with this ID and this rowversion,
set the state to deleted, call SaveChanges."
>> Got it.
>> In a WPF world where we're always connected,
and I've already retrieved it,
and I do have it in memory,
and there is a button that says "Delete,"
then I'm going to call Remove,
because I'm not recycling the DbContext on every call.
>> Right. Okay. Perfect.
>> You can also do this with modified and added.
It adds a layer of complexity, it's not worth it.
Just using the regular Update and Add
is what I find to be the best way.
But again, which way you delete depends
on whether you're in a web world or a WPF style world.
>> All right.
>> So that is persisting data in a nutshell.
Honestly, there's not a whole lot more to it.
What I wanted to build too,
if this were the crescendo of this masterpiece that we have
of the EF Core series for beginners or for people new to EF Core,
is because of the ChangeTracker,
because of all the plumbing work that is done by EF Core,
when it actually comes time to do the work of adding,
and removing, and updating entries,
you see how easy it is.
I pull a record back from the database,
I make some changes on it,
I call the update method to
tell EF Core that I want to update this,
and then I call SaveChanges,
and all of that plumbing work has done for us.
Now, what I can concentrate on as a line of business developer
is what the business wants to see in their application.
>> Right. Fantastic. All right.
So that wraps up our beginning,
our new to overview of Entity Framework Core.
>> Correct.
>> We will start a new series sometime soon
where we get into more advanced topics. But this is awesome.
Again, all of the code is up on GitHub.
There'll be links to it in the show notes.
I hope you guys have enjoyed this.
Let us know what you think as always,
and we will see you next time on Visual Studio Toolbox.
[MUSIC]