确定实体是否附加到 dbContext 的最合理方法是什么?

发布于 2024-11-07 23:43:52 字数 218 浏览 0 评论 0原文

当我尝试将实体附加到上下文时,出现异常

已经有一个具有相同键的对象 存在于 ObjectStateManager 中。这 ObjectStateManager 无法跟踪 多个对象具有相同的键

这是预期的行为。

但我想知道 ObjectStateManager 如何知道这一点? 我想先自己做一下这个检查

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

千仐 2024-11-14 23:43:52

如果您使用 DbContext API(您提到了 ef-code-first),您可以简单地使用:

context.YourEntities.Local.Any(e => e.Id == id);

或更复杂的

context.ChangeTracker.Entries<YourEntity>().Any(e => e.Entity.Id == id);

如果使用 ObjectContext API,您可以使用:

context.ObjectStateManager.GetObjectStateEntries(~EntityState.Detached)
                          .Where(e => !e.IsRelationship)
                          .Select(e => e.Entity)
                          .OfType<YourEntity>()
                          .Any(x => x.Id == id);

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);
涙—继续流 2024-11-14 23:43:52

这是一个扩展方法,用于从上下文中获取对象,而不必担心它是否已经附加:

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;
}

只需调用:

UserProfile user = dbContext.UserProfiles.GetLocalOrAttach<UserProfile>(u => u.UserId == userId, () => new UserProfile { UserId = userId });

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 });
绿光 2024-11-14 23:43:52

检查

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

连接前

check

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

before attaching

硬不硬你别怂 2024-11-14 23:43:52

请注意,如果您的上下文中禁用了更改跟踪,则询问 ObjectStateManagerChangeTracker 可能会返回该对象不在 中ObjectContext 即使它实际上已经在那里了。因此,如果您尝试附加此类对象,则会引发异常。

context.Set<T>().Local.Any(e => e.Id == id);

如果更改跟踪被禁用,则有效事件。

如果您不知道对象的类型,有多种方法,要么使用反射定义方法,要么使用像这样的其他技术
int GetIdOf(objectentity){...}

或者您定义一个类使用的接口,例如

public interface IMyEntity
{
    int Id{get;set;}
}

并以这种方式使用它:

context.Set(e.GetType()).Local.Cast<IMyEntity>().Any(e => e.Id == id);

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);
无声静候 2024-11-14 23:43:52

您可以使用“Any”扩展方法查询 dbContext:

bool alreadyInDB = dbContext.Entity.Where(a=>a.ID==myEntity.id).Any();

you can query the dbContext with the "Any" extension method:

bool alreadyInDB = dbContext.Entity.Where(a=>a.ID==myEntity.id).Any();
别挽留 2024-11-14 23:43:52

如果您像我一样从 EF Core 延迟加载场景到达这里,其中导航属性通过 DbSet.Include() 子句填充到数据层中,同时实体附加到 DbContext,然后该实体分离并传递到业务层,请考虑将类似的内容添加到 DbContext.OnConfiguring(DbContextOptionsBuilder optionsBuilder) 方法中:

optionsBuilder.ConfigureWarnings(warn => warn.Ignore(CoreEventId.LazyLoadOnDisposedContextWarning));

错误将被忽略,并且将返回最初 Include()d 的值。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文