Ok, so I thought I had the detached entity state in LINQ to SQL figured out, only to have that thrown back into my face. Basically the word from Microsoft is that if you have an entity that was loaded through a LINQ to SQL DataContext at any point it cannot be re-attached to the DataContext.
Note that all the stuff below works fine with 'connected' entities that are attached to a single instance of the DataContext. The following only applies to scenarios where the entity needs to get detached from the data context such as in Web Services or even in middle tier scenarios where the data gets passed around.
So, I figured I can safely get away with something like the following. And the following indeed works:
TimeTrakkerContext context = new TimeTrakkerContext();
EntryEntity entity = context.EntryEntities.Single(en => en.Pk == 110);
context = null; // kill the context
entity.TimeOut = DateTime.Now;
context = new TimeTrakkerContext(); // create a new one
context.EntryEntities.Attach(entity, true);
context.SubmitChanges();
The code above works currently. You can save the entity in the new context. This is easy to abstract as well so this could be abstracted in a middle tier transparently and I've done just that in my generic base Save() method of the business layer.
However last night as I was screwing around with a few more complex objects I noticed that this only works with a single level entity. If any child entities are updated it doesn't work properly:
TimeTrakkerContext context = new TimeTrakkerContext();
EntryEntity entity = context.EntryEntities.Single(en => en.Pk == 110);
// *** Kill the old context
context = null;
// *** Update single entity
entity.TimeOut = DateTime.Now;
// *** Update a related entity
entity.ProjectEntity.CustomerEntity.Company = "Summa LP III";
// *** Create a new context
context = new TimeTrakkerContext();
// *** re-attach the entity to the new context
context.EntryEntities.Attach(entity, true);
// *** Save - no error
context.SubmitChanges();
There are no errors but when this is run what happens is that a new Customer and new Project are created and the Entry is updated with a new ProjectPk (iow, the relationship is changed). Ouch!
There's a heck of a hack to work around this which is basically to force every one of the related entities to be attached as well:
entity.TimeOut = DateTime.Now;
entity.ProjectEntity.Customer.Company = "Summa LP III";
context = new TimeTrakkerContext();
context.CustomerEntities.Attach(entity.ProjectEntity.Customer, true);
context.ProjectEntities.Attach(entity.ProjectEntity, true);
context.EntryEntities.Attach(entity, true);
context.SubmitChanges();
and that actually works.
I posted a message on the MSDN forums last night and I got a note back that that apparently Microsoft is not planning on supporting this scenario at all. According to Matt Warren if an entity has been attached to one DataContext it cannot be attached to another one, even if the old context is no longer alive.
The problem here is that there is no way to detach an entity.
I'm not sure what the use case is but it seems the only way that this can be done is to create a new instance and then copy values over. I don't really understand the premise here though. Entities are POCO objects - and they hold no state, how is L2SQL even keeping track that an entity was once attached? The entity has no state. Whatever state there might have been is on the now defunct DataContext. How could it possibly know the difference between a new object entity and one that was created off another context.
IAC, while I'm sure that this isn't an easy problem to solve, it is solvable even if it's not efficient by copying properties if necessary. This isn't rocket science but it should be handled at the L2S framework level that knows EXACTLY what needs to be copied and updated or cleared. How hard could it be to provide a .Detach method and have an .Attach that can copy the state so it syncs?
If what Matt says is indeed the way it's going to be, that's really beyond lame and makes LINQ to SQL little more than a toy. I really hope I'm misunderstanding, but the inability to detach and attach entities easily is pretty crucial if you don't use two tier data scenarios with UI accessing the data directly.
I understand that LINQ to SQL is supposed to be the low end tool with the Enterprise Framework being the high end tool. Ok, so it looks like current builds of the EF have EXACTLY the same problems with attaching and detaching. I just don't get how you can build any sort of data access framework and not address this scenario. We've only been building distributed applications for what - nearly 10 years now?
Other Posts you might also like