附加类型' X'的实体失败,因为另一个相同类型的实体

时间:2015-05-20 12:45:07

标签: c# entity-framework

我偶然发现了我的代码中的一个奇怪的错误。以前哪个有效,但现在有效。

我正在使用EF6来编辑具有某种关系的实体。 为了不编辑关系我附加'他们(见示例代码)。

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'的实体失败,因为同一类型的另一个实体已具有相同的主键值。使用'附加'方法或将实体的状态设置为“未更改”#39;或者'修改'如果图中的任何实体具有冲突的键值。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,请使用'添加'方法或“添加”#39;实体状态跟踪图形,然后将非新实体的状态设置为“未更改”。或者'修改'酌情。

引入此行是因为所有C实体都是静态实体。我从不想要创建一个C.如果我删除此行,每次我将A添加到A;创建了一个C.这是不可取的。

额外信息:
A有一个B< s的列表 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

4 个答案:

答案 0 :(得分: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更改为查找
  • 从上下文获取A

首先我删除了附加C,结果创建了一个新实体。 所以我改变了这个改变。

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

答案 1 :(得分:5)

请查看以下链接https://msdn.microsoft.com/en-us/data/jj592676.aspx

如果您知道某个实体已经存在于数据库中但可能已对其进行了更改,那么您可以告诉上下文附加实体并将其状态设置为Modified。例如:

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();

    }
}

答案 2 :(得分: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)中。

希望这有帮助!

答案 3 :(得分:-1)

将实体附加或设置为修改后的didn不能处理我的案例,但这对我有用:

public int GuardarAsociacionesBonos(List<O_Bono_ConfiguracionPago> bonos)
{
    if (!bonos.Any()) return bonos.Count;

    using (var contexto = new TouchERPEntities())
    {
        foreach (var bono in bonos)
        {
           var existe = contexto.O_Bono_ConfiguracionPago.SingleOrDefault(x => x.IDBono == bono.IDBono && x.IDConfiguracionPago == bono.IDConfiguracionPago);
            if (existe != null && existe.IDBonoConfigPago != 0)
            {
                bono.IDBonoConfigPago = existe.IDBonoConfigPago;
                contexto.Entry(existe).CurrentValues.SetValues(bono);
            }
            else
            {
                contexto.O_Bono_ConfiguracionPago.Add(bono);
            }

        }

        contexto.SaveChanges();

    }

    return bonos.Count;
}