我偶然发现了我的代码中的一个奇怪的错误。以前哪个有效,但现在有效。
我正在使用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()方法。仅在循环(导入)中调用方法时才会出现此错误。在处理第一条记录时没有问题。但是我在第一个记录之后收到了错误。
我已经阅读了这些问题和答案,但他们并没有为我工作:
答案 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();
}
}
变更摘要:
首先我删除了附加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;
}