检查实体框架中是否存在实体的一般方法?

Yuc*_*uck 57 c# generics entity-framework-4.1

类似于检查实体框架中是否存在对象的最佳方法?

我正在寻找一种通用的方法来检查一个实体DbSet.像这样的东西,这是行不通的:

private DbContext DbContext { get; set; }

private DbSet<T> DbSet { get; set; }

public Boolean Exists(T entity) {
    return ((from item in this.DbSet
             where item == entity
             select item).Count() > 0);
}
Run Code Online (Sandbox Code Playgroud)

该行where item == entity适用于LINQ to SQL,但显然不适用于LINQ to Entities.由于实体可能具有不同的密钥,因此我们不能将它们全部继承自具有已知密钥的公共抽象以进行比较.

我可以做到这一点,但我担心将异常作为验证过程的表现这也不起作用,因为只要实体被分离,OriginalValues就无法获得属性:

public Boolean Exists(T entity) {
    try {
        var current = this.DbContext.Entry(entity).OriginalValues;
        // Won't reach this line if the entity isn't in the database yet
        return true;
    }
    catch (Exception ex) {
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

Lad*_*nka 85

如果实体存在,您是否需要通用方法来检查实体是否按上下文或通用方式加载查询数据库?

对于前一种情况使用:

public bool Exists<T>(T entity) where T: class
{
    return this.Set<T>().Local.Any(e => e == entity);
}
Run Code Online (Sandbox Code Playgroud)

对于后一种情况使用(它也将检查加载的实体):

public bool Exists<T>(params object[] keys)
{
    return (this.Set<T>().Find(keys) != null);
}
Run Code Online (Sandbox Code Playgroud)

编辑:

EF代码首先不应该访问这种信息,但可以获得实体密钥的名称.我认为这样的事情应该有效:

var objContext = ((IObjectContextAdapter)dbContext).ObjectContext;
var objSet = objContext.CreateObjectSet<T>();
var keyNames = objSet.EntitySet.ElementType.KeyMembers.Select(m => m.Name);
Run Code Online (Sandbox Code Playgroud)

但这一切都没有意义.您需要通用方法,但您的实体不会共享必要的信息以允许通用方法.现在你说你甚至不知道关键值.使用这种"通用"方法将需要反射和手动构建表达式树.

  • 使用`context.Set <T>().Local.SingleOrDefault(e => e == entity);`有效,但当然要求我将整个集合读入内存 - 实际上我在大多数情况下无法做到这一点的情况.和`context.Set <T>().查找(键);`工作,但正如我所提到的,我不会总是知道传入实体的键.除非有办法询问EF使用哪些属性作为钥匙? (4认同)
  • 有些答案不仅回答了问题,还指出了一个你不知道它存在的新门.我知道的"本地"财产,现在,谢谢. (4认同)

Yuc*_*uck 21

感谢@Ladislav让我朝着正确的方向前进.这是通用Exists()方法的代码.

我想指出,这不需要反思,似乎表现得相当好.我唯一不感兴趣的是它TryGetObjectByKey()具有附加找到的实体的副作用.由于我不想Exists()有这种无意的结果,所以如果找到它,我必须分离该实体.

public Boolean Exists(T entity) {
    var objContext = ((IObjectContextAdapter)this.DbContext).ObjectContext;
    var objSet = objContext.CreateObjectSet<T>();
    var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);

    Object foundEntity;
    var exists = objContext.TryGetObjectByKey(entityKey, out foundEntity);
    // TryGetObjectByKey attaches a found entity
    // Detach it here to prevent side-effects
    if (exists) {
        objContext.Detach(foundEntity);
    }

    return (exists);
}
Run Code Online (Sandbox Code Playgroud)

  • 但是`Attach`和`SaveChanges`将不会保存任何更改,因为附加使实体处于"Unchanged"状态.在"Attach"之后,必须将状态设置为"Modified".但这是无效的,因为所有列都将更新,无论它们是否确实发生了变化.为避免这种情况,您必须将原始文件加载到上下文中并在属性级别上应用更改.但是你不需要`存在',因为它只会导致实体加载两次.当`entity`有导航属性和相关对象必须更新时,你会做什么?我对一般的"保存"方法有疑问. (3认同)
  • @Yuck:不,那是错的.它没有更新.如果数据库中有一个`User`,````= 1和`Name` ="Paul",然后*将*一个`User`实体与`Id` = 1和`Name` ="Mary"连接到上下文然后直接调用`SaveChanges`,数据库中带有`Id` = 1的`User`仍然有名称"Paul".(我也在谈论EF 4.1,但没关系,这种行为是EF核心的东西.) (3认同)
  • @Yuck:我只是谈论"如果" - 例句,而不是"其他" - 当然.我不知道这怎么可以保存任何东西,它甚至不会向数据库发出任何SQL命令.从这里引用:http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity- states.aspx**...请注意,如果调用SaveChanges而不对附加实体进行任何其他操作,则不会对数据库进行任何更改.这是因为实体处于Unchanged状态......**我可以从我自己的测试中确认这一点. (2认同)