 On today's Visual Studio Toolbox, part five of our Entity Framework Core Series, Phil is going to put the CUD in CRUD. Hi, welcome to Visual Studio Toolbox. I'm your host Robert Green and joining me is Phil Jepixi. Hey Phil. Hey Robert, how are you? I'm great. This is part five of our Entity Framework Core Series. Correct. This is part five of the Intro to Entity Framework Core Series. The next parts will be the Advanced Entity Framework Core Series. Okay. This is the wrap-up of our introduction. In the previous episode, we looked at querying data. In this episode, we should probably look at modifying data, adding, editing, deleting. Correct. All right. All right. We talked about the advantages of the Change Tracker in episode two, and that it is tracking for all those things that change, get added, deleted, modified, so that when we call save changes on the DB context, then it all wraps up for us, creates the SQL and goes and does its thing. 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-sharp construct, and then I have this other helper method called should execute the transaction. The reason why I'm spending time to explain this is because if you remember from earlier episodes, I said when you call save changes on a DB context, 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. Let's go down here to this method, and it should be very familiar code. If you use transactions in ADO.NET, what I'm saying is here is my transaction on the database facade that's attached to the DB context. Remember the DB context 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. This is showing how you can use your own transaction with EF Core. Again, if I didn't do this and I just call save changes, then it would happen in its own transaction. Does that make sense, Robert? Yes. Okay. So if I want to add an item, we should probably call save changes. So here I create a new person object, and I add it into the DB set that's person. That's the specialized collection on the context that holds all the person objects. When I call save changes, that's going to happen inside a transaction by itself. But in our case, it's actually going to 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 executes any server-side things. That is, for example, row version, fields or computed columns, I should say, and sequence. So if the primary ID, the primary key was a sequence, what comes back on this person object are those fields. So it's a two-step process, calls into the server, does its changes, but then it returns basically scope identity and populates 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 add range. 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. Now, if we had a unique constraint on first name or last name, this would obviously fail and 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 DB update exception 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 email address collection, and as we've said throughout this show, we should have called that email address says, but we're adding this email address into the collection. What I can do is just call add on that person, and it also knows that this email address was added to that person collection, that will build the appropriate SQL to add all the objects all the way down. So add the object graph for you. Now, to delete an item, very simply, we're going to go back to the change tracking, and we're going to go into the delete entity right here, and we call remove. Now there's a couple of 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 showed 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, and actually I want to copy it from up here. Usually we have these pre-baked, but sometimes we go off script, and in this case, we're going off script. So here is another option that I don't have it in the collection, and 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 save changes, and it will still execute the deleted statement. Then the remove doesn't literally, that doesn't send the SQL back either until you call save changes. Right. Correct? Okay. Yeah. So nothing happens in the entire world until save changes is called. Yeah. Okay. Then to edit, we simply make some changes, and then we can call context.person.updates, person, and then we still have to call save changes, and it'll update that record. Right. So it's really pretty simple. Right? Yeah. The thing that you want to remember is that save changes will save everything that's in the change tracker and marked as has some change. So if I had a customer that was changed and a person that was changed, and an employee that was added, and I call save changes, 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 were 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 transaction, or sorry, 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 the deleted status and call save changes, and then add, and then there's the range version of all these, add range, update range, delete range, and that works on an IE numerable. 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, if I am caught, let me add some notes here, right? This must be in memory. This isn't in memory, and what I mean is retrieved from database. Okay. So if you, 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 do the state to deleted. Is that correct? So, no. Let me back up because I want to talk about the difference between a web world and an 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. 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 has to, it's got the primary key from the form collection has 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 DB set 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 all I want to make the distinction. If I'm in the web world, I've done a get, right? Now I'm disconnected. Yes, the data is displayed, but I do not have it in the DB context because at the end of that request, that DB context gets recycled. I see. So now I'm doing a post. So on the post, through the DI container within ASP on a core, it's creating a new instance of that DB context or getting it from the pool, the DB context pool. But either way, it's clean. There's nothing in it. So in the web world, I use this paradigm right here, almost exclusively. I've got the ID, I've got the row version 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 row version set the state to deleted call save changes. Got it. 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's a button that says delete, then I'm going to call remove because I'm not recycling the DB context on every call. Right. Okay. Perfect. You can also do this with modified and added. It adds a layer of complexity that'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, and honestly, there's not a whole lot more to it. What I wanted to build to, if you were, 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 change tracker, 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, right? 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 save changes, and all that plumbing work is done for us. 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 two 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.