How to copy an entity from one context (inheriting from DbContext
) to another?
Everything I have found up to works only for ObjectContext
but not for the DbContext
or uses DbContext
but does not work.
For example, I have found/tried:
- Uses ObjectContext: CloneHelper on CodeProject
- Setting LazyLoadingEnabled to false results in not filling the ICollection<> properties (foreign-keys)
- Setting ProxyCreationEnabled to false results in keeping the ICollection<> properties as null (foreign-keys)
- Entry<>.State=Detached results in not filling the ICollection<> properties (foreign-keys) if set before attaching the property, or blocks clearing Id if set later on.
- AsNoTracking() results in an exception (one of the following, depends whether inserting to the remote context first an item from the parent's ICollection<> property or first the parent):
- Parent: The object in the 'ModelFirstSub_First_Target' role cannot be automatically added to the context because it was retrieved using the NoTracking merge option. Explicitly attach the entity to the ObjectContext before defining the relationship.
- Item: The object could not be added or attached because its EntityReference has an EntityKey property value that does not match the EntityKey for this object.
I will use it for two purposes:
- Copy everything from one database to another (the destination will be a clone of the original; a local copy of a remote database). Ids of objects should be preserved.
- Add or update selected entities from one database to another (upstreaming changes in the local cache to the remote origin). Ids of the newly created objects does not need to be persisted.
How to perform that?
EF 6.1.1, .NET 4.5.2, C#
Here is the test code which tries to simulate the second action (upstreaming changes back to the remote database):
var addedFirst = localContext.Firsts.AsNoTracking().Single(m => m.Id == 4);
var updatedFirst = localContext.Firsts.AsNoTracking().Single(m => m.Id == 2);
addedFirst.Items.First().Id = 0;
addedFirst.Items.First().FirstId = 0;
addedFirst.Id = 0;
remoteContext.FirstItems.Add(addedFirst.Items.First());
remoteContext.Firsts.Add(addedFirst);
var originalFirst = remoteContext.Firsts.Single(m => m.Id == 2);
var originalItem = originalFirst.Items.First();
originalItem.Title = updatedFirst.Items.First().Title;
Here is the model:
public class ModelFirstBase
{
public virtual int Id { get; set; }
public virtual ICollection<ModelFirstSub> Items { get; set; }
public virtual string Title { get; set; }
}
public class ModelFirstSub
{
public virtual int Id { get; set; }
public virtual int FirstId { get; set; }
public virtual ModelFirstBase First { get; set; }
public virtual string Title { get; set; }
}
PS: The properties needs to be kept virtual as the model is shared with the production app which benefits from dynamic proxies.