How To Update The All The Chnaged Filds Entity Framework Asp.net Core
Although we've been using information technology for over iii years at my job developers are still incorrectly using Entity Framework's .AddOrUpdate()
. I've also seen incorrect usages of in open source projects as well. Maybe information technology'south the way Microsoft named it, only a programmer with little background in Entity Framework too as Entity Framework Migrations could look at that name and accept it literally to mean "if the record does not exist, insert it, if it does exist, update it".
Although that is what AddOrUpdate
does, it's the "Update" part of AddOrUpdate that is misunderstood. If y'all do non supply a fully populated entity to AddOrUpdate<T>
via an earlier read you will end upwardly losing data, or said another way:
unless the add together or update is a ane:1 with the tabular array you lot're updating in the database, you will lose data
This post will take a look at the existing .AddOrUpdate
implementation in Entity Framework 6.x, and then expect to see how Entity Framework 7 (Cadre) is attempting to handle the concept of "add together or update".
EF 6 AddOrUpdate
If you look at the master Entity Framework 6 code that does things similar Add and Remove, you'll notation these alive in the System.Data.Entity
namespace, only at that place is no AddOrUpdate
there. That's because the AddOrUpdate
method actually lives in the [DbSetMigrationsExtension]
(https://github.com/aspnet/EntityFramework6/blob/602bc779baa598fecf6f1ad9b94d76e8d30a9c15/src/EntityFramework/Migrations/DbSetMigrationsExtensions.cs) course, because it's intended usage is for EF Migrations. That being said, that does not hateful y'all tin can't use it as office of writing EF code, but it does hateful you accept to understand it before doing and then.
Permit's accept a look at an example of using AddOrUpdate
incorrectly, and what information technology could mean for your information.
EF6: Naive Usage of AddOrUpdate
Given a Client grade:
public course Customer { public int Id { get; set; } public string FirstName { get; prepare; } public string LastName { go; set;} public string Address { get; set;} public cord City {get; set; } public string State { get; set;} public string Zip { become; prepare; } }
nosotros take one Customer in our d
](http://world wide web.michaelgmccarthy.com/content/images/2016/08/existingcustomer.png)
Let'south say nosotros have an MVC activeness method that returns a CustomerViewModel
:
public class CustomerViewModel { public int Id { become; set; } public cord FirstName { go; set; } public string LastName { get; prepare;} } [HttpGet] public async Chore<IActionResult> Edit(int customerId) { var customer = context.Client.Unmarried(ten => x.Id == customerId); return View(new CustomerViewModel { Id = customer.Id, FirstName = customer.FirstName, LastName = customer.LastName }); }
Back in the UI, we only permit the editing of FirstName and LastName for the customer (basically, the CustomerViewModel
is optimized for this). Then nosotros post back to our controller:
[HttpPost] public async Task<IActionResult> Edit(CustomerViewModel customerViewModel) { var customer = new Client { Id = customerViewModel.Id, FirstName = customerViewModel.FirstName, LastName = customerViewModel.LastName }; context.AddOrUpdate(customer) context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); }
NOTE: nosotros're not querying the database beginning here to get the Customer, because AddOrUpdate already queries the database for u.s.a.. Besides, continue in mind that this is an naive instance of showing how AddOrUpdate'south API can exist used incorrectly.
In the Edit Postal service ActionMethod, we create a Customer, map the values from the ViewModel, and then call context.AddOrUpdate. Permit's have a look at the](http://www.michaelgmccarthy.com/content/images/2016/08/addorupdatelostourdata.png)
This is not at all what was expected! Why have nosotros lost the data that was non involved every bit part of the update when we provided an Id of a known Customer to the customer we passed to AddOrUpdate?
It's because Entity Framework does not magically figure out which backdrop have inverse vs. which properties accept not inverse, it takes the entity, and if it exists, enters the entity into the database as information technology'southward currently populated. If the entity does not exist, it inserts it into the database.
Information technology'south true in the Edit POST ActionMethod we could first execute a read (select) to get the client from persistence, and so invoke AddOrUpdate
on that client, simply like I said before, AddOrUpdate already does a read operation every bit function of its job, and then writing that code is essentially round-tripping to the database for the same information twice.
[HttpGet] public async Task<IActionResult> Edit(CustomerViewModel customerViewModel) { //VERY BAD, DON'T Exercise THIS!!! var customer = context.Customers.Single(x => 10.Id == customerViewModel.Id); customer.FirstName = customerViewModel.FirstName; customer.LastName = customerViewModel.LastName; context.AddOrUpdate(client) context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); }
This arroyo is wrong and should exist considered an anti-pattern.
How Exercise I Fix It?
The easiest way to stop this behavior, is to get rid of the CustomerViewModel class and work with the Customer entity class straight. Using this approach:
[HttpGet] public async Job<IActionResult> Edit(int customerId) { var customer = context.Customer.Single(x => x.Id == customerId); return View(customer); }
In our UI, we're still allowing editing of FirstName and LastName, but carry the rest of the customer'due south values through hidden fields in the razor view.
The Mail service method volition take a Client every bit a parameter to the method and since this Customer is the aforementioned Customer that was originally provided by the read in the GET method, it's safe to pass this to AddOrUpdate as all the properties were are populated from the the initial read.
[HttpPost] public async <IActionResult> Edit(Client customer) { context.AddOrUpdate(customer) context.SaveChangesAsync(); render RedirectToAction(nameof(Index)); }
Nosotros're obviously making assumptions about optimistic concurrency here, but taking that out of the picture, we've simplified the Get and Post action methods by but working direct with Customer instead of mapping between a ViewModel and a Customer.
Returning the Client to the view and using hidden fields to store the values we don't desire to allow to be editable, then posting back to the Edit action method allows us to supply the Customer along with the fields that we allowed someone to change (FirstName and LastName) to AddOrUpdate and permit information technology do the piece of work for us. If y'all are working on a UI that does not require any .Join on .Include statements, then just passing back the entity itself is a very uncomplicated thing to do, and results in less code written and no mapping code to write.
If y'all need to use a ViewModel (and the only fourth dimension you should employ i is when yous are working on the read side of multiple entities), then don't utilize AddOrUpdate. You'll need to handle checking for the being of one or more entities in the database (read), and then deciding if you'll insert or update those entities based on what comes back from the read.
EF 7/Core: AddOrUpdate Is Missing, At present What? (hint: write your ain)
And then far, it looks like in that location are no plans for EF 7/Core to add AddOrUpdate functionality. Reading here and here, information technology looks like people are request for information technology, only it's still unknown whether or not information technology volition be included.
I regularly contribute to an open source projection called AllReady that only went through the RTM upgrade of .NET Core. This was an interesting upgrade in the fact that is inherently changed the mode Entity Framework worked under the covers. For a full breakdown of the difference, and the change the projection had to make, check out this fantabulous write up by Steven Gordon (another regular contributor): Exploring Entity Framework Core i.0.0 RTM Changes.
Since at that place is no AddOrUpdate for EF 7/Core, Shawn Wildermuth wrote an AddOrUpdate implementation for AllReady as he atomic number 82 the charge on upgrading the project to RTM:
public static class ContextExtensions { public static void AddOrUpdate(this DbContext ctx, object entity) { var entry = ctx.Entry(entity); switch (entry.Country) { case EntityState.Discrete: ctx.Add(entity); break; example EntityState.Modified: ctx.Update(entity); break; case EntityState.Added: ctx.Add(entity); interruption; instance EntityState.Unchanged: //item already in db no need to exercise anything break; default: throw new ArgumentOutOfRangeException(); } } }
Note the usage of EntityState, and how that is used a switching mechanism for which method should exist called from DbContext.
Just to analyze, using this extension method still forces the consuming code to query for the being of an entity with the same id first, then proceed from at that place. This does not function similar EF 6's AddOrUpdate.
Here is an example of code using the AddOrUpdate extension method:
var customer = context.Customers.SingleOrDefault(c => c.Id == customerId) ?? new Customer(); //update some backdrop context.AddOrUpdate(customer); context.SaveChanges();
We're using the null-coalescing operator to create a new Client for united states of america if we don't find an existing Client in the database for the given customerId. We and so delegate to AddOrUpdate to figure out whether to add together or update this entity.
In Conclusion
We've washed a walk through of two AddOrUpdate implementations based on EF half dozen and EF seven in this blog postal service.
- EF 6's AddOrUpdate is an "all or nothing" performance that is subversive and needs to be understood in order to use correctly
- EF seven does non accept AddOrUpdate yet, but there are existing Issue'south opened on GitHub to include information technology in EF 7. We briefly examined a hand-rolled AddOrUpdate implementation for EF seven based on EntityState
Long story brusque, AddOrUdpate is incredibly useful, especially when working direct with an Entity Framework tracked model, only if yous don't know what yous're doing, AddOrUpdate can get you in a lot of trouble. Use GitHub, read the source code and understand the ramifications of AddOrUpdate before beginning to use information technology.
Subscribe to Michael McCarthy
Get the latest posts delivered right to your inbox
Source: https://www.michaelgmccarthy.com/2016/08/24/entity-framework-addorupdate-is-a-destructive-operation/
Posted by: mullinscriesuck.blogspot.com
0 Response to "How To Update The All The Chnaged Filds Entity Framework Asp.net Core"
Post a Comment