是否可以检查对象框架中是否已将对象附加到数据上下文?


86

当尝试通过以下方式附加已附加到给定上下文的对象时,出现以下错误context.AttachTo(...)

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

有没有办法实现以下目标:

context.IsAttachedTo(...)

干杯!

编辑:

Jason概述的扩展方法很接近,但不适用于我的情况。

我正在尝试使用另一个问题的答案中概述的方法来做一些工作:

如何使用Linq to Entities从表中删除一个或多个行,而无需先检索行?

我的代码看起来像这样:

var user = new User() { Id = 1 };
context.AttachTo("Users", user);
comment.User = user;
context.SaveChanges();

这工作正常,除非当我为该用户执行其他操作时,我使用相同的方法并尝试附加虚拟User对象。这失败了,因为我之前已经附加了该虚拟用户对象。我该如何检查?

Answers:


57

这是我最终得到的结果,效果很好:

public static void AttachToOrGet<T>(this ObjectContext context, string entitySetName, ref T entity)
    where T : IEntityWithKey
{
    ObjectStateEntry entry;
    // Track whether we need to perform an attach
    bool attach = false;
    if (
        context.ObjectStateManager.TryGetObjectStateEntry
            (
                context.CreateEntityKey(entitySetName, entity),
                out entry
            )
        )
    {
        // Re-attach if necessary
        attach = entry.State == EntityState.Detached;
        // Get the discovered entity to the ref
        entity = (T)entry.Entity;
    }
    else
    {
        // Attach for the first time
        attach = true;
    }
    if (attach)
        context.AttachTo(entitySetName, entity);
}

您可以这样称呼它:

User user = new User() { Id = 1 };
II.AttachToOrGet<Users>("Users", ref user);

这非常有效,因为就像context.AttachTo(...)您每次都可以使用上面引用的ID技巧一样。您最终将获得先前附加的对象或您自己的附加对象。调用CreateEntityKey上下文可确保它很好且通用,并且即使在没有进一步编码的复合键下也可以使用(因为EF已经可以为我们做到这一点!)。


我当时遇到了类似的问题,这解决了我的问题-太好了,欢呼!+1
RPM1984年

4
当将字符串参数替换为实体所属集合的选择器函数时,效果会更好。
贾斯珀

1
知道为什么(T)entry.Entity有时返回null吗?
Tr1stan 2011年

1
我不知道应该将我的entitySetName设置为什么。我一直在例外。我真的很沮丧,因为我要做的就是删除一个我不想处理太多隐藏的废话的用户,这使我的应用程序崩溃了。
朱莉(Julie)

1
如果T已经存在IEntityWithKey,您难道不能只使用它的entity.EntityKey属性而不是重建它还是需要猜测/提供EntitySetName
drzaus

54

一个更简单的方法是:

 bool isDetached = context.Entry(user).State == EntityState.Detached;
 if (isDetached)
     context.Users.Attach(user);

1
这对我有用,只是我不得不使用“ EntityState.Detached”而不是“ detached” ...
Marcelo Myara

22
嗯,尝试了您的解决方案,但对我来说isDetached是正确的,但是当我尝试将条目附加到上下文时仍然出现相同的错误
Prokurors 2014年

优良的品味。即使它与我的通用存储库一起使用。
ATHER

5
@Prokurors我最近才知道的原因MOSH的检查是不够的,以防止错误,则是.Include()“编辑导航性能也尝试当你打电话要连接.Attach或设置其EntityStateEntityState.Unchanged-他们会如果任何实体是指冲突同一实体。我还没有弄清楚如何仅附加基础实体,所以我不得不重新设计该项目,以便为每个“业务交易”使用单独的上下文,就像它设计的那样。我会在以后的项目中注意这一点。
Aske B.

18

尝试以下扩展方法(未经测试且可以直接使用):

public static bool IsAttachedTo(this ObjectContext context, object entity) {
    if(entity == null) {
        throw new ArgumentNullException("entity");
    }
    ObjectStateEntry entry;
    if(context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry)) {
        return (entry.State != EntityState.Detached);
    }
    return false;
}

鉴于您在编辑中描述的情况,您可能需要使用以下重载,该重载接受EntityKey而不是对象:

public static bool IsAttachedTo(this ObjectContext, EntityKey key) {
    if(key == null) {
        throw new ArgumentNullException("key");
    }
    ObjectStateEntry entry;
    if(context.ObjectStateManager.TryGetObjectStateEntry(key, out entry)) {
        return (entry.State != EntityState.Detached);
    }
    return false;
}

要根据EntityKey您的情况进行构建,请遵循以下准则:

EntityKey key = new EntityKey("MyEntities.User", "Id", 1);

您可以使用属性(来自interface )EntityKey从的现有实例中获取。UserUser.EntityKeyIEntityWithKey


这很好,但是在我的情况下对我不起作用...我将详细更新问题。ps你想布尔不是布尔值,并且是静态的,但是除了那个很棒的扩展方法之外!
2009年

@joshcomley:我认为您可以使用TryGetObjectStateEntry接受EntityKey而不是的重载来解决object。我做了相应的编辑。让我知道这是否无济于事,我们将回到绘图板上。
杰森

啊,刚才看到的-我正在研究刚刚发布的答案中概述的解决方案。+1为您提供帮助和指示!!
09年

任何想法,为什么我得到一个错误,细节在这里stackoverflow.com/questions/6653050/...
Joper


0

这不能直接回答OP的问题,但这是我解决问题的方法。

这适用于使用DbContext而不是的用户ObjectContext

    public TEntity Retrieve(object primaryKey)
    {
        return DbSet.Find(primaryKey);
    }

DbSet.Find方法

查找具有给定主键值的实体。如果上下文中存在具有给定主键值的实体,则将立即返回该实体,而无需向商店提出请求。否则,向商店请求具有给定主键值的实体,并且如果找到该实体,则将该实体附加到上下文并返回。如果在上下文或存储中未找到实体,则返回null。

基本上,它返回给定对象的附加对象,primaryKey因此您只需要对返回的对象应用更改即可保留正确的实例。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.