Building APIs with C# and ASP.NET Core

Adding & Removing Objects

Spencer Schneidenbach

Spencer Schneidenbach

Aviron Software, Microsoft MVP
Building APIs with C# and ASP.NET Core

Check out a free preview of the full Building APIs with C# and ASP.NET Core course

The "Adding & Removing Objects" Lesson is part of the full, Building APIs with C# and ASP.NET Core course featured in this preview video. Here's what you'd learn in this lesson:

Spencer implements the adding and removing of objects from the database through the DbContext Add and Remove methods. Change tracking, a feature of EF Core that allows you to track changes to your entities, is also discussed. It compares the current state of the entity to the original state of the entity when it was read from the database.

Preview
Close

Transcript from the "Adding & Removing Objects" Lesson

[00:00:00]
>> Spencer Schneidenbach: So we went over querying in EF Core, which I would say that between that and adding and removing objects, querying is by far the more nuanced thing. So definitely worth paying attention to and kind of learning the ins and outs and quirks of it. So let's go back to our post and put endpoints and actually add and remove objects from our database, let's refactor, let's go back and refactor those.

[00:00:22]
So I'm gonna go to the next solution folder, so we were on for E, so we need to go to for, let's see for F, perfect, okay, all right. So first things, first, we're going to go to our employee controller, we're going to scroll down and we're going to see that our comments are still there.

[00:00:44]
We're going to go ahead and delete that one and put this, put the comment back for the rest of them for now, and we have two lines that we need to write, pretty cool. They are dbContext.Employees.Add, and then dbContext.SaveChanges, I mentioned earlier that dbContext was a unit of work pattern, in addition to being a repository pattern.

[00:01:08]
So if you had made multiple changes to the database, the SaveChangesAsync represents that one unit of work to actually take care of all of that. So very straightforward, not a lot to do, and then we can actually kill this await task, completed task, because we now have a method that actually has an await call, which is to dbContext, which is pretty great.

[00:01:31]
So next we are going to go here, and we're going to go to our UpdateEmployee endpoint, do that, and we'll just comment that out, and we will do pretty much the exact same thing. Except this time we've already pulled this thing from memory, or we will here in a second, we'll go ahead and async Task of IActionResult, and then we will await, dbContext dot and we'll just use FindAsync now.

[00:02:06]
It's a little FindAsync, that's good, okay, we do need to say Employees.FindAsync, and then, because this is already part of the change tracker. I mentioned earlier that dbContext will track our changes for us, we will say, await dbContext.SaveChangesAsync, and we will that should be pretty good. Now, according to this flow, we were like, we'll run the tests and wait a failure because we forgot to uncomment out our validator, we're just gonna go ahead and validate that real quick.

[00:02:41]
We're gonna go ahead and refactor our validator here real quick as well, so we're gonna go to our UpdateEmployeeRequest, we're gonna uncomment that out as well. And we are going to destroy all the references to IRepository, AppDbContext appDbContext, boom, and then we will say hover over that, click there, control dot or sorry command dot, command period, and assign the field.

[00:03:13]
We'll rename it per Spenser's style guidelines, self inflicted ones, and then we'll move that there, so, it's nice and organized. And then, of course, we earlier declared this async method knowing that we would have to refactor it to talk to a specific database, so we'll take out this call and we kept it async.

[00:03:34]
So we will go ahead and say await appDbContext.FindAsync, we'll go ahead and just call that, and we will say id, again, missing the dot employees, and there we go, we got it. So, pretty straightforward refactor so far, let's go ahead and uncomment and run our tests and see what happens, so, we got our CreateEmployees and our UpdateEmployees.

[00:04:06]
>> Spencer Schneidenbach: So, we'll leave everything off, but the benefits control shift tilda, and I'll say dotnet test, and you're gonna see that all the tests that we created, all pass, which is exactly what we want to see, so, that's really awesome. So we did all of that, but I do want to discuss one thing, a little bit of magic in EF Core, if you will, and I do want, and I think it's important, to bring up, which is the Change Tracker.

[00:04:31]
So the change tracker is notoriously a little slow, it's really only slow when you're dealing with lots and lots of objects. But as objects are loaded into memory in EF Core, you will find that the Change Tracker will track all of those objects, and even if they don't need an update, they will attempt to be updated.

[00:04:49]
So you can instruct EF Core to track changes manually, you can turn off that change tracking, and make it so that you have to explicitly tell entity framework what objects to update. In my opinion, in new projects, I prefer to turn change tracking off, I do it simply because I prefer explicit code, that said, if you leave it on and it works for you, I think that's totally fine.

[00:05:19]
But me, personal preferences, I tend to turn it off where I can, so I'm gonna update our call to add dbContext to say program, add dbContext and do that. And we're gonna say query tracking behavior, no Tracking, which is going to affect the update endpoint specifically. And it's gonna update and it's gonna affect it because we have not instructed it to track that object, it's actually coming, it's being queried, but EF Core is no longer tracking it.

[00:05:55]
So if we run our tests, we'll see it pass, okay, nothing broke well, there you go does this mean it didn't change nothing? It means our tests aren't sufficient, that's right, if you take a look at our UnitTest for UpdateEmployee, all it says is, is there a success status code?

[00:06:14]
So in this case, while we have tests and the tests are really good at protecting ourselves from weird behavior, they're not sufficient in this case. So let's go ahead and update them to ensure that it actually did update what we expected to update, so I'm gonna copy this stuff in, and then I will paste it in here, it's gonna need an import, so I will command dot and import that.

[00:06:40]
And now what we're doing is we're changing John Doe's street to 123 Main Smoot, and a Smoot, by the way, is a unit of measurement look it up, it's true. And we are going to load it from the database and then see, in addition to this, we are also going to change our query tracking behavior inside of our method here.

[00:07:03]
So options.UseQueryTrackingBehavior is no tracking, so it'll turn off change tracking there, and then we will run our tests, and you'll see that now our 123 Main Smoot is not 123 Main Smoot, it's 123 Main Street. That's because we turned off automatic change tracking, so we can fix this very easily, one line of code, if we go here to our put endpoint, our UpdateEmployee endpoint.

[00:07:33]
All we have to do is notate to the dbContext that this thing needs to be updated and the way you do that is, you go dbContext.Entry, you give it the object that is the entry that you want to update. So in this case, it's the existingEmployee, and then you say, State equals Modified, okay?

[00:07:57]
So, now we've marked this thing as this entity is now being tracked, we are explicitly turning off automatic change tracking, and we've explicitly turned it off. And now we're explicitly telling this instance of the dbContext, track this object, so now we're gonna rerun our tests, dotnet test, and we're gonna see that they all pass now.

[00:08:18]
Now it's updating as it should, which is exactly what we want, so one other thing that you can do is you can actually turn on AsTracking, and if you have and if you have an application that you're running. You can actually inside of a single query, you can turn it off as, as no tracking in case you're doing a read only search, so you can do that by await dbContext.Employees, and you can say AsTracking.

[00:08:49]
And then you FindAsync method, because FindAsync operates on DB set doesn't exist anymore, so we'll just simply turn it to SingleOrDefaultAsync. And then we'll say, E, E dot Id equals that id, and then we'll go down here, remove this, and you'll see that because we've explicitly for this query turned on change tracking that our object still gets updated.

[00:09:13]
So that's basically instructing EF Cord to say yes, track these objects that I'm getting from this query, and the opposite is true. If you have change tracking automatic change tracking turned on, you can say AsNoTracking, so you can not have to track objects that you don't need to track.

[00:09:31]
So last thing we're gonna go over is removing an object from the database, so I'm going to go down here and I'm just gonna paste this below my update method. And we're gonna go through it super quick, because there's really not a lot of complication with it, so in this case, we're basically taking and we're creating a delete method.

[00:09:53]
So we're gonna send a delete command to this API with that id, and it's going to call this DeleteEmployee method with the id taken from the route, as we would expect. We'll return NotFound if that thing is not found, but otherwise we will go dbContext.Employees.Remove, and then our SaveChangesAsync, will then delete that from the database, very straightforward.

[00:10:18]
And you can see here that we can add in a couple of tests to check for the NotFound, and then we can also create our new employee here and then delete them after we've created them. So boom, we'll go here to our tests, make our project even more beefy, make it me even more awesome by making sure that all of our tests are there and we are covering all the behaviors of our code.

[00:10:42]
So we'll go control shift tilda, and we'll go dotnet test, and we'll see that everything worked, we added a new employee object to our context here in our test, and then we're immediately turning around and deleting it. And what's this employee's name? Meow Garida, it's one of my clients refers to me as Meow Gerita, it's a long story all right, we got our test passing, so now we've created our queries and we've also added and removed objects in the database.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now