找出实体是不是附加到 dbContext 的最合理方法是啥?

Posted

技术标签:

【中文标题】找出实体是不是附加到 dbContext 的最合理方法是啥?【英文标题】:what is the most reasonable way to find out if entity is attached to dbContext or not?找出实体是否附加到 dbContext 的最合理方法是什么? 【发布时间】:2011-08-27 08:44:57 【问题描述】:

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

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

这是预期的行为。

但我想知道 ObjectStateManager 是如何知道的? 我想自己做这个检查之前

【问题讨论】:

我怀疑它会询问具有相同键的实体的当前上下文,您当然可以执行相同的var exists = (dbContext.entities.Where(e=>e.ID == myEntity.ID).Count() > 0); 或类似的操作 +1 进行验证,但我宁愿使用 Any insted of Count 这怎么可能是预期的行为?所以你每次都要检查一个对象是否已经在上下文中? wtf? @IanWarburton 是的。实体框架确实留下了很多松散的结局。本质上它只对简单的数据访问有用。 【参考方案1】:

检查

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

附加之前

【讨论】:

我添加的实体已分离,但可能已经有实体加载到具有相同 ID 的 dbcontext 中。【参考方案2】:

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

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

【讨论】:

【参考方案3】:

如果您使用的是 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);

【讨论】:

谢谢!奇迹般有效。但如果我不知道类型,也许你有任何建议如何做到这一点。我根据类型设置 - context.Set(typeX)。 我希望这仍然有效:context.Set(type).Local.Any(...) 但是您可以在实体中覆盖 equals 并简单地调用 Any(e =&gt; e.Equals(yourEntity)) @achekh - 我在 EF5 中尝试过,但它不起作用,如果您阅读关于 builin Equals 方法的评论,它会显示“两个 System.Data.Entity.Infrastructure.DbEntityEntry 实例如果它们都是同一 System.Data.Entity.DbContext 上同一实体的两个条目,则认为它们是相等的。” - 意味着它们需要引用同一个实体对象。 @Bond - 你是对的,不幸的是 DbEntityEntry.Compare 不考虑键,只考虑条目和实体引用。那么进行比较的最快方法是强制比较 EntityKey 实例,您可以使用 ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetObjectStateEntry(entity).EntityKey【参考方案4】:

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

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

【讨论】:

【参考方案5】:

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

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

如果更改跟踪被禁用,则工作事件。

如果您不知道对象的类型,则有多种方法,您可以使用反射或其他类似的技术定义方法 int GetIdOf(object entity)...

或者你定义你的类使用的接口,比如

public interface IMyEntity

    int Idget;set;

并以这种方式使用它:

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

【讨论】:

.Set 是一种方法,因此第一个 sn-p 在 之后需要括号【参考方案6】:

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

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

错误将被忽略并返回最初包含()d的值。

【讨论】:

以上是关于找出实体是不是附加到 dbContext 的最合理方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

EF6 无法将分离的实体附加到上下文

如何更新在 DbContext 之外修改的实体?

实体框架代码优先 - DbContext 上没有 Detach() 方法

在 EF Core DbContext 中,附加方法不起作用

有没有办法找出哪个表列导致与实体框架的转换出错?

如何从 DBContext(实体框架核心)中的 App.config(不是 .net 核心应用程序)读取值