在我的编辑页面上,我收到此错误:
附加类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。
所以,我研究了那个并来到this。
这导致我想要使用Auto-Mapper来简化事情。
这是我的代码:
if (db.TableName.Find(value.ID).stringProperty.Equals(value.stringProperty, StringComparison.CurrentCultureIgnoreCase))
{
Table inContextVariable = db.TableName.Find(value.ID);
Mapper.Initialize(config => config.CreateMap<ModelName, ModelName>());
Mapper.Map<ModelName, ModelName>(value, inContextVariable);
db.Entry(inContextVariable).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
这导致我出现这个错误:
附加信息:操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。
这个表有1个外键,当我调试外键的值为11时,我很难理解这个错误。
UPDATE
想象一下,如果我有一个数据库表,其中包含一个包含人员信息的记录,例如:
| ID | First Name | Last Name | Email |
----------------------------------------------------------------------------
1 John Doe [email protected]
2 Christopher Columbus | [email protected] |
依此类推......现在,由于一个人只能拥有1个电子邮件地址,因此在创建/编辑记录时必须对其他用户采取保护措施。
例如,如果我要使用此应用程序并且我想创建一个帐户..我应该无法使用数据库表中已存在的电子邮件地址成功填写表单。我应该收到一条错误消息说些什么
该电子邮件地址已存在!请输入另一个电子邮件地址
这是代码:
if (db.TableName.Any(x => x.Email.Equals(value.Email, StringComparison.CurrentCultureIgnoreCase)))
{
ModelState.AddModelError("Email", "This email already exists!");
return View(value);
}
现在......当用户成功创建帐户时会发生什么...然后将他们的帐户电子邮件地址编辑为已存在的内容?应该出现相同的错误消息。
但是当该用户创建了他们的帐户时,他们的电子邮件地址现在位于数据库表中..因此,当他们使用原始电子邮件转到“编辑”页面时,他们点击“保存”,代码将针对所有电子邮件地址检查该电子邮件在数据库中,它将看到它已经在数据库中导致显示错误消息(请更改您的电子邮件.etc)..
所以我需要尝试创建一种有效的方法来检查用户何时在编辑页面上,并点击“保存”原始电子邮件,它不会告诉他们更改他们的电子邮件。
所以我有这个代码:
if (db.TableName.Find(value.ID).Email.Equals(value.Email, StringComparison.CurrentCultureIgnoreCase))
{
var inContextVariable = db.TableName.Find(value.ID);
MappingMethods.MapModelName(inContextVariable , value);
\\ without the mapping.. I receive the error saying 'Attaching an entity of type failed because another entity of the same type already has the same primary key value.'
db.Entry(inContextVariable).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
这是我在这里使用的基本逻辑
Table inContextVariable = db.TableName.Find(value.ID);
if(!inContextVariable.Email.Equals(value.Email, StringComparison.CurrentCultureIgnoreCase))
{
// user input a new email so make sure it doesn't match anyone else
if(db.TableName.Any(x => x.Email.Equals(value.Email, StringComparison.CurrentCultureIgnoreCase) && x.ID != value.ID))
{
// user's new email DOES match someone else's email so handle it
}
else
{
// no match so do the update
}
}
无需处理外部其他因为他们没有更改他们的电子邮件,所以你什么都不做!
注意:您将在这里遇到竞争条件!如果两个人都在接近同一时间提交,那么支票可能会通过,但他们最终都会收到相同的电子邮件。就像让我们说他们都变成[email protected]
但第二人的检查是在第一个人拯救之前完成的。但是,这就是网络应用的生命。
在@nurdyguy的帮助下,我提出了这个想法。我创建了另一个名为MappingMethods
的类,它保存了这个方法:
public static void MapModelName(ModelName inContext, ModelName outOfContext)
{
inContext.ID = outOfContext.ID;
inContext.stringProperty= outOfContext.stringProperty;
inContext.OwnerID = outOfContext.OwnerID; \\ foreign key
inContext.DateCreated = outOfContext.DateCreated;
}
然后在我的编辑操作中:
if (db.TableName.Find(value.ID).stringProperty.Equals(value.stringProperty, StringComparison.CurrentCultureIgnoreCase))
{
var inContextVariable = db.TableName.Find(value.ID);
MappingMethods.MapModelName(inContextVariable , value);
db.Entry(inContextVariable).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
这很有效。
automapper的整个想法是避免静态类中的这些方法。想象一下,如果你有100个属性? (仅举例)。请问你复制粘贴obj1.prop1 = obj2.prop1?
首先,我建议创建一个视图模型 - smth。这将代表您的模型的视图。是的,您可以使用automapper创建对象的副本,但它不是为它设计的。
试着像这样做: