附加类型为“X”的实体失败,因为已存在另一个相同类型的实体。

18

我在我的代码中遇到了一个奇怪的错误。之前它是可以工作的,但现在有时候会出问题。

我正在使用EF6来编辑一些关联实体。 为了不编辑这些关系,我使用了'Attach'方法(请参见示例代码)。

public void EditA(A ThisIsA, B ThisIsB)
    {
        using (var Context = new LDZ_DEVEntities())
        {
            Context.As.Attach(ThisIsA);

            var b = Context.Bs.FirstOrDefault(x => x.BId == ThisIsB.BId);
            //var b = Context.Bs.Find(ThisIsB.BId);

            if (b != null)
                Context.Bs.Attach(b);
            else
                b = ThisIsB;

            if (b.C != null)
                Context.Cs.Attach(b.C);

            ThisIsA.Bs.Add(b);

            Context.SaveChanges();

        }
    }

我已经简化了名称。

以下是该行:

Context.Cs.Attach(b.C);

抛出此错误:

附加类型为'C'的实体失败,因为另一个具有相同主键值的类型相同的实体已经存在。当使用“Attach”方法或将实体状态设置为“Unchanged”或“Modified”时,如果图中的任何实体具有冲突的键值,则可能会发生这种情况。这可能是因为某些实体是新的,尚未收到数据库生成的键值。在这种情况下,请使用“Add”方法或“Added”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“Unchanged”或“Modified”。

添加此行是因为所有的C实体都是静态实体。我永远不想创建C。如果我删除此行,每次我将B添加到A时,都会创建一个C。这是不可取的。

额外信息:
A拥有B的列表
B有一个C

我的软件中多个位置调用了此EditA()方法。只有在循环(导入)调用该方法时才会出现此错误。在处理第一条记录时没有问题。但是我在第一条记录之后的记录中遇到了错误。

我读过这些问题及其答案,但对我无效:

  1. ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value

  2. Attaching an entity of type failed because another entity of the same type already has the same primary key value


1
如果A有一个B列表,而B只有一个C,那么你只需要附加A。Bs和Cs将自动插入... - Fabio Luz
C是静态实体,我从不想插入它们。只有在B不存在时才需要插入B。 - Robin Gordijn
“静态实体”是什么意思?根据您的代码,C语言实体有一个数据集。它们怎么可能是“静态”的呢? - Kryptos
静态的意思是,有一组C实体。它们永远不会被创建/更新/删除。 - Robin Gordijn
可能有帮助的相关帖子:https://dev59.com/qGAg5IYBdhLWcg3wx9XG - Vaibhav
3个回答

19

我修复了它。

在Fabio Luz的答案中,他说:

//如果A已经从上下文中加载
//不要附加它
//如果它是在上下文之外创建的
//Context.Entry(ThisIsA).State = EntityState.Modified;

这让我思考,所以我将我的代码编辑为:

public void EditA(A ThisIsA, B ThisIsB)
{
    using (var Context = new LDZ_DEVEntities())
    {
        var a = Context.As.Find(ThisIsA.AId);

        //var b = Context.Bs.FirstOrDefault(x => x.BId == ThisIsB.BId);
        var b = Context.Bs.Find(ThisIsB.BId);

        if (b != null)
            Context.Bs.Attach(b);
        else
            b = ThisIsB;

        if (b.C != null)
            Context.Cs.Attach(b.C);

        a.Bs.Add(b);

        Context.SaveChanges();

    }
}

更改摘要:

  • 将FirstOrDefault更改为Find
  • 从上下文中获取A

起初,我删除了C的Attach,结果这创建了一个新实体。因此,我撤销了这个更改。

特别感谢Fabio Luz的帮助!我没有你的帮助就做不到这一点。


1
不客气,伙计!感谢你的赞美。将自己的帖子标记为答案,有助于将来其他人的解决问题。:D - Fabio Luz
这确实帮了我很多——我在一个单独的变量中加载要编辑的实体,没想到更改跟踪器会跟踪不同版本并将其进行比较... 一旦我去掉了其他引用,那么对EntityState.Modified的更改就像魔术一样奏效了。谢谢。 - keithl8041
没有问题,keithl8041,请帮忙投票支持这篇文章,这样我们就可以帮助更多的人找到答案,从而帮助他们。 - Robin Gordijn

7
请查看下面的链接 https://msdn.microsoft.com/zh-cn/data/jj592676.aspx 如果你有一个已经存在于数据库中但可能被修改过的实体,你可以告诉上下文附加该实体并将其状态设置为已修改。例如:
var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" }; 

using (var context = new BloggingContext()) 
{ 
    context.Entry(existingBlog).State = EntityState.Modified; 

    // Do some more work...  

    context.SaveChanges(); 
}

注意:您不需要对所有对象(A、B和C)都执行此操作,只需对A执行即可。
编辑1
根据您的评论,请尝试以下操作:
//check if 
var _b = Context.Bs.Find(ThisIsB.BId);

if (_b != null)
  //b doesn't exist, then add to the context
  //make sure that the primary key of A is set.
  //_b.PrimaryKeyOfA = someValue;
  Context.Bs.Add(_b);
else
 //b already exists, then modify the properties
 //make sure that the primary key of A is set.

Context.SaveChanges();

编辑2

我没有测试过,但它应该可以工作。

public void EditA(A ThisIsA, B ThisIsB)
{
    using (var Context = new LDZ_DEVEntities())
    {
        //if A has been loaded from context
        //dont attach it
        //if it has been created outside of the context
        //Context.Entry(ThisIsA).State = EntityState.Modified;

        var _b = Context.Bs.Find(ThisIsB.BId);

        if (_b == null)
        { 
            _b = ThisIsB;
        }

        ThisIsA.Bs.Add(_b);

        Context.SaveChanges();

    }
}

不幸的是,这并没有解决问题。该方法用于将B添加到A中。所以我只想记录A和B之间的更改关系。如果B不存在,B将被创建(发生在A.Bs.Add(B)中)。 - Robin Gordijn
请看编辑1 - Fabio Luz
我认为我们在这里缺少了一些东西。你能否准确地解释一下你想做什么?我之所以这样说,是因为你的编辑方法有3个参数,这对我来说看起来很奇怪...这应该是不必要的。 - Fabio Luz
好的,事情变得清晰了...每次调用EditaA时,第一个参数(objectA)已经存在于数据库中,对吗? - Fabio Luz
请查看编辑2 - Fabio Luz
显示剩余4条评论

2

另一种方法是根据您的情况,只需分离实体状态。

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Modify(Model model)
{

if (model.Image == null)
{
Model item = db.Model.Find(model.Name);

// Get the Content needed:
model.Image = item.Image;

// Detach the Comparison State:
db.Entry(item).State = EntityState.Detached;
}

if (ModelState.IsValid)
{
db.Entry(model).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}

return View(model);
}

通过这样做:db.Entry(item).State = EntityState.Detached;,EntityFramework的状态仍然保持完好,您可以将更改保存到数据库(db)中。
希望这可以帮助您!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接