在我的编辑页面上,我收到了此错误:
附加类型的实体失败,因为同一类型的另一个实体已具有相同的主键值。
所以,我研究了这个问题并来到了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时,我很难理解这个错误。
更新
想象一下,如果我有一个数据库表,其中包含包含人员信息的记录,例如:
| ID | First Name | Last Name | Email |
----------------------------------------------------------------------------
1 John Doe john.doe@test.com
2 Christopher Columbus | chris.columbus@test.com |
依此类推......现在,由于一个人只能拥有1个电子邮件地址,因此在创建/编辑记录时必须对其他用户采取保护措施。
例如,如果我要使用此应用程序并且我想创建一个帐户..我应该无法使用数据库表中已存在的电子邮件地址成功填写表单。我应该收到出现类似
的错误消息该电子邮件地址已存在!请输入其他电子邮件地址
以下是代码:
if (db.TableName.Any(x => x.Email.Equals(value.Email, StringComparison.CurrentCultureIgnoreCase)))
{
ModelState.AddModelError("Email", "This email already exists!");
return View(value);
}
现在......当用户成功创建帐户时会发生什么情况呢?然后将编辑他们的帐户电子邮件地址转到已存在的内容?应该出现相同的错误消息。
但当该用户创建了自己的帐户时,他们的电子邮件地址现在位于数据库表中..所以当他们转到“编辑”页面时,使用原始电子邮件,并且他们点击“保存”,代码将检查该电子邮件对数据库中的所有电子邮件地址,并且它将看到它已经在数据库中导致显示错误消息(请更改您的电子邮件.etc).. < / p>
因此,我需要尝试创建一种有效的检查方法,以便在编辑页面上看到 时,然后点击“保存”原始文件电子邮件,它不会告诉他们更改他们的电子邮件。
所以我有这个代码:
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");
}
答案 0 :(得分:2)
这是我将在这里使用的基本逻辑
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
}
}
无需处理外部其他内容,因为他们没有更改电子邮件,因此您不做任何事情!
注意:您将遇到竞争条件!如果两个人都在接近同一时间提交,那么支票可能会通过,但他们最终都会收到相同的电子邮件。就像让我们说它们都改为x@y.com
但第二人的检查是在第一个人的保存之前完成的。但是,这就是网络应用的生命。
答案 1 :(得分:0)
在@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");
}
这很有用。
答案 2 :(得分:0)
automapper的整个想法是避免在静态类中使用这样的方法。想象一下,如果你有100个属性? (仅举例)。你会复制粘贴obj1.prop1 = obj2.prop1?
首先,我建议创建一个视图模型 - smth。这将代表您的模型的视图。是的,您可以使用automapper创建对象的副本,但是 它不是为它而设计的。
尝试像这样做:
将您的模型映射到view-model和virce-verce,如下所示:
Mapper.CreateMap() .ForMember(d =&gt; d.DateCreate,d =&gt; d.MapFrom(x =&gt; x.DateCreated));