4

I'm using EF 4, POCO. The task is the following: having a long set of data of DomainObject type that need to be taken from Database and updated (add, update, delete operations) in a disconnected mode. How can I push the updated set back to the database later on? Lets assume there were no parallel changes to the table since we load data. I know context.SaveChanges() do the update, but the question is how to put all the changes back to DbSet from kinda List, or maybe there is a way to work directly with DbSet in disconnected mode? Thanks!

YMC
  • 4,925
  • 7
  • 53
  • 83

2 Answers2

7

Self-tracking entities are feature of EDMX + ObjectContext API so if you have your mapping / code dependent on DbContext API (you mentioned DbSet) you can't use them. Self-tracking entities are implementation of change set pattern (like old data set). Some reference on SO:

If you don't use Self-tracking entities the answer is pretty simple - you must say EF what changes you did. Once using detached entities you must attach entities back to a new context and manually say exactly what you did = what is inserted, updated, deleted and how did you change relations. The key component in this process is ObjectStateManager in ObjectContext API (context.ObjectStateManager) orDbChangeTracker in DbContext API (context.ChangeTracker). This is extremely hard when dealing with relation so many of us usually using another process: we load the entity graph again and merge changes from detached entity graph to attached entity graph. Some references:

Be aware that whole your process can be pretty slow depending on number of updates you want to do. The reason is that EF doesn't support command batching so each insert, update, delete is executed in a separate round trip to the database. If you update 100.000 complex objects you can expect that execution of SaveChanges takes several minutes.

Edit:

In some very special scenarios where you work only with entities without relations you can use a trick to transfer information about "changes":

public void ProcessChanges(IEnumerable<DomainObject> entities)
{
    foreach(var entity in entities)
    {
        if (entity.Id == 0)
        {
            // New entity
        }
        else if (entity.Id > 0)
        {
            // Modified entity (you cannot say if entity war really modified,
            // you must update it always).
        }
        else
        {
            // Use negative Id (origId * -1) to mark entity as deleted
            // reverse Id and delete entity
        }
    }
}

This works only for flat objects with simple key.

Community
  • 1
  • 1
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • Thanks for the post, you seem to have a good experience in the field. Right, I'm using POCO, so STE does not help me. Let me clarify, how can I know what was inserted or updated having List of DomainObject? Looks like I need to have 2 separate lists for updated and added records or extend my DomainObject to store operation info, right? – YMC May 05 '11 at 22:04
  • Yes you need to pass that information either as second data structure or in the domain object (that will actually mean implementing your own STE). It is obviously something you usually don't want to do and that is the reason why we often use the approach I described: loading entities from DB and merge changes. But I don't say that it is the only way to do that for very simple scenarios you can use some tricks to transfer information about modifications - I will add sample to my answer. – Ladislav Mrnka May 05 '11 at 22:08
  • Let me clarify my task. Maybe you can propose solution: 1)I do not need send data between tires.2) It's one plain Domain Entity, no graph.3) By disconnected data set I meant regular plain list of my domain objects. May be I can work directly with DbSet? The purpose is to request Db once, then do some complex processing in memory (not to deal with DB), requesting, updating and adding data to the in-memory structure, which would take much more time to do directly on DB. Finally push the changes to DB, or just override them to the DB, replacing the whole set of data (table in DB terms). – YMC May 05 '11 at 22:19
  • Well, and why it must be disconnected? What is wrong on using entities still connected to the context and performing inserts and deletes directly on `DbSet`? – Ladislav Mrnka May 05 '11 at 22:51
  • I saw database request each time I try to find something in DbSet. How can I avoid it? How can ask DbSet load all the data from Db and work with in-memory snapshot? – YMC May 05 '11 at 22:54
  • You must run the query to load all data and either work with result of the query or use `Local` on `DbSet` – Ladislav Mrnka May 05 '11 at 22:55
  • Got it. Thanks for all your responses. Can not vote your post up unfortunately as I'm new here and have not enough points allowing to vote up. However as soon as I have them, I'll be back and vote you up – YMC May 05 '11 at 23:48
  • BTW, I can not work with result of the running query coz I need the added data being in one collection to query them in any next step of the processing. However DbSet.Local works fine, exactly what I need: it prevents database round-trips and allows to access just added data to the context in the same time. – YMC May 06 '11 at 01:50
  • @YMC: Once you are owner of the question (original poster) you can also accept one answer if it helps. – Ladislav Mrnka May 06 '11 at 07:39
  • @LadislavMrnka Hi, quick question: When you say "merge changes from detached entity" you do this manually or with an existing method? – AJC Feb 22 '12 at 21:02
  • @AJC: Merge must be usually done manually because EF provides method to "merge" only single entity - not entity graph. – Ladislav Mrnka Feb 22 '12 at 23:06
  • @LadislavMrnka That's what I thought... Do you have any generic method or algorithm to help you perform the merge? (I am working on one but is proving very difficult for complex relationship graphs)? – AJC Feb 22 '12 at 23:16
  • @AJC: No I don't have any. I don't use generic approach. – Ladislav Mrnka Feb 22 '12 at 23:26
-1

Have you tried self-tracking entities ?

http://msdn.microsoft.com/en-us/library/ff407090.aspx

http://blogs.msdn.com/b/adonet/archive/2010/06/02/working-with-sets-of-self-tracking-entities.aspx

http://msdn.microsoft.com/en-us/library/ee789839.aspx#Y2300

http://blogs.u2u.be/diederik/post/2010/05/18/Self-Tracking-Entities-with-Validation-and-Tracking-State-Change-Notification.aspx

Alexandre Brisebois
  • 6,599
  • 12
  • 50
  • 69
  • Thanks for the hint. I was not aware of this feature. Let me check it up. – YMC May 05 '11 at 01:13
  • you can also find a great example of self-trcking entities in this book : Entity Framework Recipes: A Problem-Solution Approach if you look it up on google, you will have a preview and you may be able to view the the right examples – Alexandre Brisebois May 05 '11 at 01:15
  • Look for sections 9-5, 9-6, 9-7 and 9-9 in "Entity Framework Recipes: A Problem-Solution Approach" – Alexandre Brisebois May 05 '11 at 01:24