maxlego
maxlego

Reputation: 4914

what is the most reasonable way to find out if entity is attached to dbContext or not?

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

Answers (6)

Dubbs777
Dubbs777

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

KramFfud
KramFfud

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

Ladislav Mrnka
Ladislav Mrnka

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

David Sherret
David Sherret

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

Bongo Sharp
Bongo Sharp

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

Kaido
Kaido

Reputation: 3931

check

entity.EntityState == System.Data.EntityState.Detached

before attaching

Upvotes: 6

Related Questions