5

I tried the following to detach a graph of entity objects, and then attach it to a new context:

// create a context
var ctx = new TestEntities();
var parents = ctx.Parents;

// populate the graph
var newParent = new Parent {Nb = 1, Title = "Parent1"};
parents.AddObject(newParent);
newParent.Children.Add(new Child {Nb = 1, Title = "Child1"});

// put all entity objects in Unchanged state before detaching
ctx.SaveChanges(); 

// detach all entity objects
foreach (var objectStateEntry in ctx.ObjectStateManager.GetObjectStateEntries(~EntityState.Detached))
    ctx.Detach(objectStateEntry.Entity);

// create a new context
ctx = new TestEntities(); 

// attach graphs to new context
foreach (var p in parents)
    ctx.Attach(p);

I have two issues with this approach:

  1. After detaching all entity objects, newParent.Children becomes empty
  2. An InvalidOperationException is raised when re-attaching saying that "An entity object cannot be referenced by multiple instances of IEntityChangeTracker".

Does anyone know how to properly detach a graph from an ObjectContext, and re-attach it to another one?

UPDATE:

Ok good news for me, I figured out how to change the underlying database connection within the same ObjectContext, so I don't need to detach/attach anymore. If anybody's interested, here's how I do it (here I use SQLite and change the database file):

var sc = ((EntityConnection)ctx.Connection).StoreConnection;
sc.ConnectionString = @"Data Source=" + newFile + ";";

I'll accept Ladislav's answer as it seems to be correct and answers my question as it was asked.

1 Answers1

3

You must create deep clone of the whole graph and attach it to another context. The deep clone is created through the serialization. The common approach is to use DataContractSerializer:

var serializer = new DataContractSerializer(typeof(Parent));
serializer.WriteObject(stream, attachedEntity);
stream.Seek(0, SeekOrgin.Begin);
detachedEntity = (Parent)serializer.ReadObject(stream);

To make this work your entities must not contain circular references (Parent has navigation property to Child and Child has navigation property to Parent) or you must use DataContract(IsReference=true) and DataMember attributes on your entities to inform serializer that it must track references to resolve circular reference issue.

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • I don't want to do a copy because I have an undo/redo stack that refers to objects in the graph, and I don't want to scrap the stack when I change the ObjectContext. Do you know of any other approach I can use to change the context, or only its connection object, while keep using the same graph? –  Jul 22 '12 at 11:22
  • 1
    In such case you must use the same context for both operations because there is currently no way to detach entity and keep its relations - that is why you must do a copy to keep them. – Ladislav Mrnka Jul 22 '12 at 12:48
  • Yes I was afraid of that, but hey, I just found out I can change the underlying connection string, and so I don't need to detach/attach to begin with. –  Jul 22 '12 at 13:28