EF Core modifies objects it tracks by setting keys and maintaining navigation properties.
As an example of why this may be a problem, let's say you start a task which will add an entity to a DbContext. If you then immediately enumerate some navigation properties of that same entity without waiting for the task to finish, you can get an InvalidOperationException. When the entity got tracked in the other thread, it might have picked up some other data from the context and changed the collection.
I'd like to avoid those issues by cloning the entities going into and out of EF Core. But I also don't wish to write a ton of unmaintainable and error-prone code for cloning the entities by hand.
Here's how far I got:
public static TEntity CloneEntity<TEntity>(this DbContext context, TEntity entity)
where TEntity : class
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (entity == null)
throw new ArgumentNullException(nameof(entity));
// A map for keeping track of already-cloned objects for circular references.
var map = new Dictionary<object, object>(ReferenceEqualityComparer.Instance);
return (TEntity)Recurse(context.Entry(entity));
object Recurse(EntityEntry entry)
{
if (map.TryGetValue(entry.Entity, out var clone))
return clone;
clone = entry.CurrentValues.ToObject();
map.Add(entry.Entity, clone);
// TODO: Recursively clone and set all the navigation properties.
return clone;
}
}
I can probably figure out how to solve the TODO-bit by reflection, but EF Core should have done all of that already and it should already have compiled methods for efficiently setting navigation properties. Is there a way to use those, similar to entry.CurrentValues.ToObject()?
As far as you are able to deliver solution by your own(I guess) I just put in here something I spent time on. It is a working draft. It may or may not satisfy your needs and may help others to find at least something as a solution.
I like expresions, so it was done using them, but still you can go purely with reflection.