How to save merged (new + changed) separate objects in Entity Framework?

What is the right and fast way to save combined new and changed individual POCO objects?

I was thinking about these methods:

private void Method_2(IList<Entity> entities) //detached entities { //This method is using SELECT to check if entity exist using (var context = new ModelContainer()) { foreach (Entity entity in entities) { var foundEntity = context.CreateObjectSet<Entity>().SingleOrDefault(t => t.Id == entity.Id); context.Detach(foundEntity); //Remove it from ObjectStateManager if (foundEntity != null)//It is modified entity { context.AttachTo("EntitySet", entity); //Attach our entity context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified); //We know it exists } else//It is new entity { context.CreateObjectSet<Entity>().AddObject(entity); } } context.SaveChanges(); } } private void Method_1(IList<Entity> entities) //detached entities { //This method doesn't select anything from DB, but i have ta call Savechanges after each object using (var context = new ModelContainer()) { foreach (Entity entity in entities) { try { context.AttachTo("EntitySet", entity); context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified); context.SaveChanges(); } catch (OptimisticConcurrencyException) { context.ObjectStateManager.ChangeObjectState(entity, EntityState.Added); context.SaveChanges(); } } } } 

When you work in a disconnected environment, you need to know which entity has been added and which has been changed - you are responsible for storing this information and providing its ObjectContext.

Well, I agree with this statement if you are in a situation where you need to use an EF code like this in EF, there is definitely something wrong with you. I chose the wrong tool for this job.

+6
c # entity-framework poco
source share
3 answers

When you work in a disconnected environment, you need to know which entity has been added and which has been changed - you are responsible for storing this information and providing its ObjectContext.

The easiest way:

 foreach (var entity in entities) { if (entity.Id == 0) // 0 = default value: means new entity { // Add object } else { // Attach object and set state to modified } } 

This example requires that you have your own generated primary key (Id).

Your method 2 is possible with some changes. No need to detach an object when loading it. Use ApplyCurrentValues ​​instead. At first, the approach with a loading object is very useful when you decide to work with object graphs instead of a single object. But in the case of an object graph, you need to perform manual synchronization. ApplyCurrentValues ​​works only for scalar (non-navigational) properties. You can try to optimize your method to load the necessary enitites in one reverse direction into the database, rather than loading objects one by one.

Your method 1 is a terrible decision. Using exceptions that occur on the database server to control program flow is a bad approach.

+4
source share

I agree with @Ladislav - Method_1 is a bad approach. Let the database raise exceptions that are caught by EF - don't try to swallow these exceptions yourself.

Being on the right track with method 1.

This is how I do it - as I also have a separate context (POCO, no change tracking, ASP.NET MVC).

BLL interface: (note that I have TPT in my model, so generics. "Post" is abstract)

 void Add(Post post); void Update<TPost>(TPost post) where TPost : Post, new(); 

The new() constraint is crucial - you will immediately understand why.

I will not show how I do "Add", because it is just, as you think - AddObject(entity);

Upgrading is the hard part:

 public class GenericRepository<T> : IRepository<T> where T : class { public void Update<T2>(T2 entity) where T2: class, new() { var stub = new T2(); // create stub, now you see why we need new() constraint object entityKey = null; // ..snip code to get entity key via attribute on all domain entities // once we have key, set on stub. // check if entity is already attached.. ObjectStateEntry entry; bool attach; if (CurrentContext.ObjectStateManager.TryGetObjectStateEntry(CurrentContext.CreateEntityKey(CurrentContext.GetEntityName<T>(), stub), out entry)) { // Re-attach if necessary. attach = entry.State == EntityState.Detached; } else { // Attach for first time. attach = true; } if (attach) CurrentEntitySet.Attach(stub as T); // Update Model. (override stub values attached to graph) CurrentContext.ApplyCurrentValues(CurrentContext.GetEntityName<T>(), entity); } } 

And it works for me.

As for the entity key, I used attributes for my domain classes. An alternative (which I am going to move) is that all my domain objects implement an interface that indicates that all domain objects should have a property called EntityKey. Then I will use this interface for my limitations. Basically, I need a dynamic way to create stub objects in a shared repository.

I personally do not like the idea of ​​"checking the identifier, if its> 0, then this is an update." Since I work with ASP.NET MVC, if I (or another developer) forget to associate the ID with the view, it will not be passed, so even if it can be an update, because ID == 0 it will be added.

I like to talk about operations. This way I can add / update a separate validation logic.

+1
source share

Perhaps take a look at the Self Tracking POCO objects. IMHO they are ideal for any scenario that requires an object branch from the context. He takes care of all the plumbing code for you.

0
source share

All Articles