AutoMapper给出了无效的操作异常

问题描述 投票:3回答:3

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

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

所以,我研究了那个并来到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");
}
c# asp.net-mvc automapper
3个回答
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

    }

}

无需处理外部其他因为他们没有更改他们的电子邮件,所以你什么都不做!

注意:您将在这里遇到竞争条件!如果两个人都在接近同一时间提交,那么支票可能会通过,但他们最终都会收到相同的电子邮件。就像让我们说他们都变成[email protected]但第二人的检查是在第一个人拯救之前完成的。但是,这就是网络应用的生命。


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");
}

这很有效。


0
投票

automapper的整个想法是避免静态类中的这些方法。想象一下,如果你有100个属性? (仅举例)。请问你复制粘贴obj1.prop1 = obj2.prop1?

首先,我建议创建一个视图模型 - smth。这将代表您的模型的视图。是的,您可以使用automapper创建对象的副本,但它不是为它设计的。

试着像这样做:

  1. 创建一个视图模型 - 它可以与您的模型相同 - 这一切都取决于您(将数据库模型传递到视图中不是一个好习惯)
  2. 将模型映射到视图模型和virce-verce,如下所示: Mapper.CreateMap()。ForMember(d => d.DateCreate,d => d.MapFrom(x => x.DateCreated));
© www.soinside.com 2019 - 2024. All rights reserved.