实体框架在使用自动映射程序后分离了虚拟对象



我已经设置了一个 MVC 环境,在其中我正在获取实体框架实体,将其映射到 DTO 以供用户修改,然后将 DTO 映射回 EF 实体。

我的 EF 类如下所示:

public class Person
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int AddressId { get; set; }
public virtual StreetAddress Address { get; set; }
/*More stuff here*/
}
//For the purpose of this example lets assume that addresses cannot be changed, they are read-only
public class StreetAddress
{
[Key]
public int Id { get; set; }
public string StreetName { get; set; }
/*More stuff here*/
}

我的 DTO 是:

public class PersonDTO
{
public int Id { get; set; }
public string Name { get; set; }
public int AddressId { get; set; }
public StreetAddress Address { get; set; }
/*More stuff here*/
}

从这里我将人员映射到人员DTO,然后再返回:

Person person = /*retrieve from database*/;
PersonDTO dto = Mapper.Map<PersonDTO>(person);
// send dto to user, user updates name, user sends dto back
// the address object is the same, but it might have been converted to JSON or something like that during transport
Person person = Mapper.Map<Person>(dto);

所以我现在有一个人准备保存到数据库。StreetAddress 对象仍然具有相同的信息,但现在 EF 将地址视为已分离。我不能使用context.Attach(Address)因为上下文已经附加了具有相同键的街道地址,并且尝试这样做会导致引发异常。

如果我选择忽略分离的地址并将此新人员添加到我的数据库中,EF 会将地址视为一个全新的对象并将其插入到数据库中,从而导致数据库中的地址行重复,相同的详细信息,新 ID。

我当前的解决方案是将 Address 对象替换为已附加到上下文的对象:

var tracked = context.ChangeTracker.Entries<Address>();
person.Address = tracked.Where(x => x.Entity.Id == person.AddressId).Select(e => e.Entity).FirstOrDefault();

有没有更好的方法可以做到这一点? 也许有一种方法可以告诉 EF,如果有一个人的地址,而地址 ID 已经在数据库中,则无需插入它?

一种方法是在Person和PersonDTO中公开您的FKAddressId

另一种方法是将实体状态设置为修改:context.Entry(person.Address).State = EntityState.Modified;

如下所示

最新更新