找出实体是不是附加到 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 => e.Equals(yourEntity))
@achekh - 我在 EF5 中尝试过,但它不起作用,如果您阅读关于 builin Equals 方法的评论,它会显示“两个 System.Data.Entity.Infrastructure.DbEntityEntry((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】:请注意,如果在您的上下文中禁用了更改跟踪,则询问ObjectStateManager
或ChangeTracker
可能会返回该对象不在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 在如果您像我一样从 EF Core 延迟加载场景到达这里,其中导航属性通过 DbSet.Include() 子句填充到数据层中,而实体附加到 DbContext 然后该实体已分离并传递给业务层,请考虑在您的 DbContext.OnConfiguring(DbContextOptionsBuilder optionsBuilder) 方法中添加类似的内容:
optionsBuilder.ConfigureWarnings(warn => warn.Ignore(CoreEventId.LazyLoadOnDisposedContextWarning));
错误将被忽略并返回最初包含()d的值。
【讨论】:
以上是关于找出实体是不是附加到 dbContext 的最合理方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
实体框架代码优先 - DbContext 上没有 Detach() 方法