AutoMapper提供无效操作异常

时间:2017-01-11 18:04:36

标签: c# asp.net-mvc automapper

在我的编辑页面上,我收到了此错误:

  

附加类型的实体失败,因为同一类型的另一个实体已具有相同的主键值。

所以,我研究了这个问题并来到了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");
}

3 个答案:

答案 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创建对象的副本,但是 它不是为它而设计的。

尝试像这样做:

  1. 创建一个视图模型 - 它可以与您的模型相同 - 这一切都取决于您(将数据库模型传递到视图中不是一个好习惯)
  2. 将您的模型映射到view-model和virce-verce,如下所示:

    Mapper.CreateMap()       .ForMember(d =&gt; d.DateCreate,d =&gt; d.MapFrom(x =&gt; x.DateCreated));