10

Given the following code:

    void MergeDbContext(DbContext aSourceDbContext, DbContext aDestinationDbContext)
    {
        var sourceEntities = aSourceDbContext.ChangeTracker.Entries().ToList();
        foreach (DbEntityEntry entry in sourceEntities)
        {
            object entity = entry.Entity;
            entry.State = EntityState.Detached;

            // check if entity is all ready in aDestinationDbContext if not attach
            bool isAttached = false;// TODO I don't know how to check if it is all ready attched.
            if (!isAttached)
            {
                aDestinationDbContext.Set(entity.GetType()).Attach(entity);
            }
        }
    }

How can I generically determine if an entity exists in the context.

Steven T. Cramer
  • 1,508
  • 1
  • 18
  • 34
  • 1
    http://stackoverflow.com/questions/6018711/generic-way-to-check-if-entity-exists-in-entity-framework – Vel Jun 04 '15 at 15:28
  • wouldn't this be complicated if there are associations and modified entities? are you planning to preserve modifications? – jjj Jun 09 '15 at 18:04
  • @jjj You can only perform 1 async query per DBContext and there is a scenario where I want to perform multiple in order to improve performance. So I create 2 DBcontext and here I am trying to merge the results. For my particular instance, these would be read-only and no changes. – Steven T. Cramer Jun 09 '15 at 20:03

1 Answers1

5

This is an extension method that looks for an object in a context's state manager by the following steps:

  • Get the type's key properties
  • Get the object's key values
  • Check if there is any entity in the context having the object's type and key values.
public static bool IsAttached<T>(this DbContext context, T entity)
    where T : class
{
    if (entity == null) return false;

    var oc = ((IObjectContextAdapter)context).ObjectContext;

    var type = ObjectContext.GetObjectType(entity.GetType());

    // Get key PropertyInfos
    var propertyInfos = oc.MetadataWorkspace
          .GetItems(DataSpace.CSpace).OfType<EntityType>()
          .Where(i => i.Name == type.Name)
          .SelectMany(i => i.KeyProperties)
          .Join(type.GetProperties(), ep => ep.Name, pi => pi.Name, (ep,pi) => pi);

    // Get key values
    var keyValues = propertyInfos.Select(pi => pi.GetValue(entity)).ToArray();

    // States to look for    
    var states = System.Data.Entity.EntityState.Added|System.Data.Entity.EntityState.Modified
                |System.Data.Entity.EntityState.Unchanged|System.Data.Entity.EntityState.Deleted;

     // Check if there is an entity having these key values
    return oc.ObjectStateManager.GetObjectStateEntries(states)
             .Select(ose => ose.Entity).OfType<T>()
             .Any(t => propertyInfos.Select(i => i.GetValue(t))
                                    .SequenceEqual(keyValues));
}

Usage:

bool isAttached = dbContext.IsAttached(entity);
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • I get the following exception on the setting of keyprops. "Mapping and metadata information could not be found for EntityType 'System.Object'." I think because the entity is of a proxy type. – Steven T. Cramer Jun 12 '15 at 20:26
  • bool isAttached = aDestinationDbContext.IsAttached(entity); Is how I am calling it ... and yes I don't know the entity type as I am trying to generically merge two contexts – Steven T. Cramer Jun 12 '15 at 20:44
  • So that would solve the exception yes but will always return false. setting a watch variable on "entity.GetType().Name" I get: "Parent_D6C692F519B2A9BA4C1E11ABFBC4086C28421AD0E3114091C3BA5A9D71089899" "entity.GetType().BaseType.Name" returns "Parent" – Steven T. Cramer Jun 12 '15 at 21:00
  • var typename = typeof(T).Name; // "Object" that does yield Object. But given I am trying to merge two generic dbcontexts how would I know the type? – Steven T. Cramer Jun 12 '15 at 21:11
  • var isProxy = ObjectContext.GetObjectType(entity.GetType()) != entity.GetType(); // evals to true – Steven T. Cramer Jun 12 '15 at 21:42
  • 1
    I couldn't leave this alone and I finally tackled it. My mistake was to think that a proxy *inherently* always returns the base type as its type. But that only because most method returning proxies *cast* them to their base types. And that's exactly what doesn't happen here. So I had to rewrite the method that discovers the key properties. – Gert Arnold Jun 16 '15 at 07:22
  • Excellent. I got busy for a couple of days doing other tasks. I also have updated your code a bit and am adding caching of keys. Will post once complete... (guessing weekend) Thanks again. – Steven T. Cramer Jun 17 '15 at 14:59