【问题标题】:Generic Way to Check If Entity Exists In Entity Framework?检查实体框架中是否存在实体的通用方法?
【发布时间】:2011-08-26 11:57:14
【问题描述】:

类似于Best way to check if object exists in Entity Framework?

我正在寻找一种通用方法来检查 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);
}

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

【问题讨论】:

  • 为了澄清想要一个通用的Exists() 方法的原因,我希望能够创建一个Save() 方法,它可以确定是否需要将实体添加到上下文中(@ 987654329@) 或附件 (UPDATE)。我没有在问题中提到这一点,因为一旦我有了Exists()Save() 就变得微不足道了。

标签: c# generics entity-framework-4.1


【解决方案1】:

您想要通用方法来检查实体是否由上下文加载,还是使用通用方法来查询数据库是否存在实体?

对于前一种情况使用:

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

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

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

编辑:

EF 代码首先不应访问此类信息,但可以获取实体键的名称。我认为这样的事情应该可行:

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

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

【讨论】:

  • 使用context.Set&lt;T&gt;().Local.SingleOrDefault(e =&gt; e == entity); 有效,但当然需要我将整个集合读入内存——实际上在大多数情况下我无法做到这一点。 context.Set&lt;T&gt;().Find(keys); 有效,但正如我所提到的,我并不总是知道传入实体的键。除非有办法询问 EF 哪些属性被用作键?
  • 一些答案​​不仅回答了问题,而且还为您指明了一扇您不知道它存在的新门。 Local 财产,我现在就知道了,谢谢。
  • 不应该像 - public bool Exists&lt;T&gt;(T entity) where T: class 吗?或者不是类,而是给出了一些其他数据类型,只是想知道。
  • 不会this.Set&lt;T&gt;().Find(keys) 加载整个实体?如果这是一个紧密的循环,这可能会对性能产生严重影响。特别是如果您只需要检查密钥是否存在。
  • 如果您使用 InMemory DbContext 可能会出现问题,将不存在的实体设置为已修改可能会导致在引发错误之前将其添加到内部内存中。因此,如果您在捕获中进行存在检查,如果您首先尝试将其设置为已修改,该实体可能会显示为存在。
【解决方案2】:

感谢@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);
}

【讨论】:

  • 但是AttachSaveChanges 不会保存任何更改,因为附加会使实体处于Unchanged 状态。您必须在Attach 之后将状态设置为Modified。但这是无效的,因为所有列都将被更新,无论它们是否真的发生了变化。为避免这种情况,您必须将原始文件加载到上下文中并在属性级别应用更改。但是你不需要Exists,因为它只会导致加载实体两次。当entity 具有导航属性并且必须更新相关对象时,您会怎么做?我对通用的Save 方法有疑问。
  • @Yuck:我只是在谈论“if”情况,当然不是“else”情况。我看不出这怎么能保存任何东西,它甚至不会向数据库发出任何 SQL 命令。从这里引用:blogs.msdn.com/b/adonet/archive/2011/01/29/… ...请注意,如果在未对附加实体进行任何其他操作的情况下调用 SaveChanges,则不会对数据库进行任何更改。这是因为实体处于未更改状态...我可以通过自己的测试确认这一点。
  • @Yuck:不,那是错误的。它不更新。如果您在数据库中有 UserId = 1 和 Name = "Paul" 然后附加 User 实体 Id = 1 和 Name = "Mary" 到上下文然后直接调用SaveChanges,数据库中Id = 1 的User 仍然有名字"Paul"。 (我也在谈论 EF 4.1,但没关系,这种行为是 EF 核心的东西。)
  • 事实证明我有一些讨厌的副作用,它允许对象以这种方式保存。我将编辑我的Save(),因为它与原始问题无关。我将开始一个新问题来跟踪进度,并希望获得更多的 cmets。感谢您的帮助@Slauma。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-02
  • 2011-08-31
  • 1970-01-01
  • 2020-08-15
  • 2010-12-20
  • 1970-01-01
相关资源
最近更新 更多