Reputation: 4914
when i try to attach entity to context i get an exception
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
This is expected behaviour.
But i would like to know how ObjectStateManager knows that? I would like to do this check by myself before
Upvotes: 74
Views: 42189
Reputation: 377
Note that if change tracking is disabled on your context, asking the ObjectStateManager
or the ChangeTracker
might return that the object is not in the ObjectContext
even if it is in fact already in there. Therefore, if you try to attach such object it will raise an exception.
context.Set<T>().Local.Any(e => e.Id == id);
works event if change tracking is disabled.
if you do not know the type of the object, there is various approach, either you define a method using reflection or other techniques like this one
int GetIdOf(object entity){...}
Or you define an interface used by your classes like
public interface IMyEntity
{
int Id{get;set;}
}
and use it this way :
context.Set(e.GetType()).Local.Cast<IMyEntity>().Any(e => e.Id == id);
Upvotes: 2
Reputation: 229
If you have arrived here, as I did, from an EF Core Lazy Loading scenario in which Navigation properties were filled in a data layer via DbSet.Include() clause(s) while the Entity was attached to a DbContext and then that Entity was detached and passed up to a business layer, consider adding something like this to your DbContext.OnConfiguring(DbContextOptionsBuilder optionsBuilder) method:
optionsBuilder.ConfigureWarnings(warn => warn.Ignore(CoreEventId.LazyLoadOnDisposedContextWarning));
The error will be ignored and the values that were originally Include()d will be returned.
Upvotes: 0
Reputation: 364269
If you are using DbContext API (you mentioned ef-code-first) you can simply use:
context.YourEntities.Local.Any(e => e.Id == id);
or more complex
context.ChangeTracker.Entries<YourEntity>().Any(e => e.Entity.Id == id);
In case of ObjectContext API you can use:
context.ObjectStateManager.GetObjectStateEntries(~EntityState.Detached)
.Where(e => !e.IsRelationship)
.Select(e => e.Entity)
.OfType<YourEntity>()
.Any(x => x.Id == id);
Upvotes: 107
Reputation: 106640
Here's an extension method for getting the object from the context without having to worry about whether it is already attached:
public static T GetLocalOrAttach<T>(this DbSet<T> collection, Func<T, bool> searchLocalQuery, Func<T> getAttachItem) where T : class
{
T localEntity = collection.Local.FirstOrDefault(searchLocalQuery);
if (localEntity == null)
{
localEntity = getAttachItem();
collection.Attach(localEntity);
}
return localEntity;
}
Just call:
UserProfile user = dbContext.UserProfiles.GetLocalOrAttach<UserProfile>(u => u.UserId == userId, () => new UserProfile { UserId = userId });
Upvotes: 13
Reputation: 9610
you can query the dbContext with the "Any" extension method:
bool alreadyInDB = dbContext.Entity.Where(a=>a.ID==myEntity.id).Any();
Upvotes: 0
Reputation: 3931
check
entity.EntityState == System.Data.EntityState.Detached
before attaching
Upvotes: 6