【发布时间】:2017-01-02 17:05:26
【问题描述】:
我有一个 Supplier.cs 实体及其 ViewModel SupplierVm.cs。我正在尝试更新现有供应商,但我收到了带有错误消息的黄屏死机 (YSOD):
操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。
我认为我知道为什么会这样,但我不知道如何解决它。这是正在发生的事情的screencast。我认为我收到错误的原因是因为当 AutoMapper 执行它的事情时这种关系丢失了。
代码
以下是我认为相关的实体:
public abstract class Business : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string TaxNumber { get; set; }
public string Description { get; set; }
public string Phone { get; set; }
public string Website { get; set; }
public string Email { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
public virtual ICollection<Address> Addresses { get; set; } = new List<Address>();
public virtual ICollection<Contact> Contacts { get; set; } = new List<Contact>();
}
public class Supplier : Business
{
public virtual ICollection<PurchaseOrder> PurchaseOrders { get; set; }
}
public class Address : IEntity
{
public Address()
{
CreatedOn = DateTime.UtcNow;
}
public int Id { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string Area { get; set; }
public string City { get; set; }
public string County { get; set; }
public string PostCode { get; set; }
public string Country { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
public int BusinessId { get; set; }
public virtual Business Business { get; set; }
}
public class Contact : IEntity
{
public Contact()
{
CreatedOn = DateTime.UtcNow;
}
public int Id { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Department { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
public int BusinessId { get; set; }
public virtual Business Business { get; set; }
}
这是我的ViewModel:
public class SupplierVm
{
public SupplierVm()
{
Addresses = new List<AddressVm>();
Contacts = new List<ContactVm>();
PurchaseOrders = new List<PurchaseOrderVm>();
}
public int Id { get; set; }
[Required]
[Display(Name = "Company Name")]
public string Name { get; set; }
[Display(Name = "Tax Number")]
public string TaxNumber { get; set; }
public string Description { get; set; }
public string Phone { get; set; }
public string Website { get; set; }
public string Email { get; set; }
[Display(Name = "Status")]
public bool IsDeleted { get; set; }
public IList<AddressVm> Addresses { get; set; }
public IList<ContactVm> Contacts { get; set; }
public IList<PurchaseOrderVm> PurchaseOrders { get; set; }
public string ButtonText => Id != 0 ? "Update Supplier" : "Add Supplier";
}
我的AutoMapper映射配置是这样的:
cfg.CreateMap<Supplier, SupplierVm>();
cfg.CreateMap<SupplierVm, Supplier>()
.ForMember(d => d.Addresses, o => o.UseDestinationValue())
.ForMember(d => d.Contacts, o => o.UseDestinationValue());
cfg.CreateMap<Contact, ContactVm>();
cfg.CreateMap<ContactVm, Contact>()
.Ignore(c => c.Business)
.Ignore(c => c.CreatedOn);
cfg.CreateMap<Address, AddressVm>();
cfg.CreateMap<AddressVm, Address>()
.Ignore(a => a.Business)
.Ignore(a => a.CreatedOn);
最后,这是我的SupplierController编辑方法:
[HttpPost]
public ActionResult Edit(SupplierVm supplier)
{
if (!ModelState.IsValid) return View(supplier);
_supplierService.UpdateSupplier(supplier);
return RedirectToAction("Index");
}
这是SupplierService.cs 上的UpdateSupplier 方法:
public void UpdateSupplier(SupplierVm supplier)
{
var updatedSupplier = _supplierRepository.Find(supplier.Id);
Mapper.Map(supplier, updatedSupplier); // I lose navigational property here
_supplierRepository.Update(updatedSupplier);
_supplierRepository.Save();
}
我已经阅读了大量内容,根据this blog post,我所拥有的应该可以工作!我也读过stuff like this,但我想在放弃 AutoMapper 以更新实体之前,我会先与读者核实一下。
【问题讨论】:
-
你应该检查 updatedSupplied 里面有什么,它有没有子属性。错误使您的子项变为空,因此外键错误。
-
在什么阶段?你的意思是在 AutoMapper 完成它的事情之后还是在它之前?如果您的意思是在此之前,一切看起来都很好,据我所知,一切都应该如此。
-
好的,这表明映射没有设置导航属性。本质上 .ForMember(d => d.Addresses, o => o.UseDestinationValue()) 不起作用。您可能需要显式创建映射配置。
-
看看this post上的第二个答案。像您一样处理映射和保留嵌套类
-
谢谢@big_water,
.ForMember(dest => dest.ID, opt => { opt.UseDestinationValue(); opt.Ignore(); });是做什么的?这就像告诉它使用 DestinationValue 但同时忽略它,这对我来说没有意义!
标签: c# entity-framework asp.net-mvc-5 automapper