FaNIX FaNIX - 2 months ago 16
C# Question

Check if entity is being tracked by Entity Framework context

I have a code block which checks whether an entity is being tracked by my context. If it is, I need to detach it. This works for a given T type.

public virtual async Task<bool> InsertOrUpdate(TE entity)
{
if (entity.Id == 0 || entity.Id == ModelState.New)
{
// attach new entity
_context.Entry(entity).State = EntityState.Added;
}
else
{
// Sometimes when you want to update a detached entity, before attempting to attach it (by setting the .State property),
// you first need to make sure the entity isn't already attached and being tracked. If this is the case, the existing entity
// needs to be detached, and the updated entity, attached.
var attachedEntity = _context.ChangeTracker.Entries<TE>().FirstOrDefault(e => e.Entity.Id == entity.Id);
if (attachedEntity != null)
{
// the entity you want to update is already attached, we need to detach it and attach the updated entity instead
_context.Entry<TE>(attachedEntity.Entity).State = EntityState.Detached;
}

_context.Entry<TE>(entity).State = EntityState.Modified; // Attach entity, and set State to Modified.
_context.Entry<TE>(entity).Property(o => o.CreatedUserId).IsModified = false;
_context.Entry<TE>(entity).Property(o => o.CreatedDate).IsModified = false;
}

return await _context.SaveChangesAsync() > 0;
}


I now need to change the method so that it fetches all objects of type IEntity within the given T entity parameter and then do the same logic for each object found, but I'm having trouble with setting the ChangeTracker.Entries as I need to set the T type to the current selected type within the foreach. I have no idea how to do this.

public virtual async Task<bool> InsertOrUpdate(TE entity)
{
//// Find all instances of IEntity within TE:
//// * IF entity is new we set State to EntityState.Added (INSERT)
//// * IF entity is existing, we set State to EntityState.Modified (UPDATE)
List<IEntity> found = FindAllInstances<IEntity>(entity);
foreach (IEntity ent in found)
{
if (entity.Id == 0 || entity.Id == ModelState.New)
{
// attach new entity
_context.Entry(entity).State = EntityState.Added;
}
else
{
// Sometimes when you want to update a detached entity, before attempting to attach it (by setting the .State property),
// you first need to make sure the entity isn't already attached and being tracked. If this is the case, the existing entity
// needs to be detached, and the updated entity, attached.
var attachedEntity = _context.ChangeTracker.Entries<TE>().FirstOrDefault(e => e.Entity.Id == entity.Id);
if (attachedEntity != null)
{
// the entity you want to update is already attached, we need to detach it and attach the updated entity instead
_context.Entry<TE>(attachedEntity.Entity).State = EntityState.Detached;
}

_context.Entry<TE>(entity).State = EntityState.Modified; // Attach entity, and set State to Modified.
_context.Entry<TE>(entity).Property(o => o.CreatedUserId).IsModified = false;
_context.Entry<TE>(entity).Property(o => o.CreatedDate).IsModified = false;
}
}

return await _context.SaveChangesAsync() > 0;
}

Answer

You could use the inner context wich is an ObjectContext.

var ctx = ((IObjectContextAdapter)_context).ObjectContext;

And then call ctx.Detach() on whatever entity you want. Fortunately this is not a generic method.

You can also get a reference to an ObjetStateManager from the ObjectContext and use it to do whatever state change you want.

More informations: https://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.objectstatemanager(v=vs.110).aspx

https://msdn.microsoft.com/en-us/library/system.data.objects.objectstatemanager(v=vs.110).aspx