0

当我尝试更新 Entity Framework Core 中的导航属性时,我遇到了一些异常行为,它将值设置为 null,然后从数据库中删除记录。

简化示例:

public class Hla : BaseEntity
{
    public string AField1 { get; set; }
    public virtual Patient Patient { get; set; }
    public int PatientId { get; set; }
}

public class Patient : BaseEntity
{
    public string Test { get; set; }
    public virtual Hla Hla { get; set; }
}

public class PatientRepository : Repository, IPatientRepository
{
    private readonly SearchAndMatchContext _context;

    public PatientRepository(SearchAndMatchContext context) : base(context)
    {
        _context = context;
    }

    public async Task<Patient> RetrieveEntity(int id)
    {
        return await _context.Patients
            .Include(t=>t.Hla)
            .FirstOrDefaultAsync(t => t.Id == id);
    }

    public  void UpdateEntity(Patient entity)
    {
        _context.Patients.Update(entity);
    }
}

导致问题的代码:

private static Hla Map(Hla original, Hla update)
{
    var  fieldsToSkip = new string[]{"Id"};
    
    PropertyInfo[] fields = update.GetType().GetProperties();

    foreach (var field in fields)
    {
        if (fieldsToSkip.Contains(field.Name))
        {
            continue;
        }

        var newValue = field.GetValue(update);
        field.SetValue(original, newValue);
    }

    return original;
}

var patient = await _patientRepository.RetrieveEntity(1);

Map(patient.Hla, new Hla
    {
        AField1 = "dfgdfg",
        PatientId = patient.Id
    });

patientRepository.UpdateEntity(patient);
await _patientRepository.UnitOfWork.SaveChangesAsync();

一旦SaveChangesAsync调用 ,Hla就会被设置为 null - 来自即时窗口的输出:

在此处输入图像描述

然而,如果我有一个简单的“直接”映射器,它似乎工作得很好。

更简单的映射器:

private static void Map(Hla current, Hla update)
{
    current.AField1 = update.AField1;
}

然后我得到即时窗口输出:

在此处输入图像描述

我有点困惑为什么会发生这种情况,这对我来说似乎没有意义。

如果我使用自动映射器等也会发生这种情况。

我错过了什么?

4

2 回答 2

2

该方法Map(Hla original, Hla update)复制所有属性(除了Hla.Id),这包括属性Hla.PatientHla.PatientId。但是来自的副本Hla update没有耐心:

var patient = await _patientRepository.RetrieveEntity(1);

Map(patient.Hla, new Hla
    {
        AField1 = "dfgdfg",
        PatientId = patient.Id
        //No patient set
    });

然后Map设置在原来的Patient <- null。接下来 EF 检测数据库中的修改、更新并应用于所有实体,包括Patient.

解决方案是从副本中排除属性PatientPatientId

private static Hla Map(Hla original, Hla update)
{
    var  fieldsToSkip = new string[]{"Id", "PatientId", "Patient"};

    PropertyInfo[] fields = update.GetType().GetProperties();
    foreach (var field in fields)
    {
        if (fieldsToSkip.Contains(field.Name))
        {
            continue;
        }
        var newValue = field.GetValue(update);
        field.SetValue(original, newValue);
    }
    return original;
}

编辑:其他解决方案是Patient从实例复制属性设置:

Map(patient.Hla, new Hla
    {
        AField1 = "dfgdfg",
        PatientId = patient.Id,
        Patient = patient
    });
于 2021-09-01T13:48:38.453 回答
-1

Map在这里似乎不是很有用。它不用于映射,它用于在实体中设置单个属性。这些字段是硬编码的,因此没有任何收获。问题的代码可以替换为:

var patient = await _patientRepository.RetrieveEntity(1);
patient.AField1 = "dfgdfg";
await _patientRepository.UnitOfWork.SaveChangesAsync();

调用Update也没有用,因为 EF 已经跟踪patient实体并将检测任何更改。Update仅对未跟踪/分离的对象才需要,以告诉 EF 开始跟踪处于该Updated状态的对象。

于 2021-09-01T14:18:31.810 回答