在 EF Core 中更新导航属性将它们设置为 null
Updating navigational properties sets them to null in EF Core
当我尝试更新 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 - 从直接 window:
输出
然而,如果我有一个简单的“直接”映射器,它似乎工作得很好。
更简单的映射器:
private static void Map(Hla current, Hla update)
{
current.AField1 = update.AField1;
}
然后我立即得到 window 输出:
我有点不明白为什么会这样,这对我来说似乎没有意义。
如果我使用自动映射器等,也会发生这种情况。
我错过了什么?
方法 Map(Hla original, Hla update)
复制所有属性(Hla.Id
除外),这包括属性 Hla.Patient
和 Hla.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 检测修改,在 DB 中更新并应用于所有实体包括 Patient
.
解决方案是从副本中排除属性 Patient
和 PatientId
:
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
});
Map
在这里似乎用处不大。它不是用来映射的,它是用来在实体中设置单个属性的。这些字段是硬编码的,因此不会有任何收获。问题的代码可以替换为 :
var patient = await _patientRepository.RetrieveEntity(1);
patient.AField1 = "dfgdfg";
await _patientRepository.UnitOfWork.SaveChangesAsync();
对 Update
的调用也没有用,因为 EF 已经跟踪 patient
实体并将检测任何更改。 Update
只需要 untracked/detached 个对象,告诉 EF 开始跟踪处于 Updated
状态的对象。
当我尝试更新 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 - 从直接 window:
然而,如果我有一个简单的“直接”映射器,它似乎工作得很好。
更简单的映射器:
private static void Map(Hla current, Hla update)
{
current.AField1 = update.AField1;
}
然后我立即得到 window 输出:
我有点不明白为什么会这样,这对我来说似乎没有意义。
如果我使用自动映射器等,也会发生这种情况。
我错过了什么?
方法 Map(Hla original, Hla update)
复制所有属性(Hla.Id
除外),这包括属性 Hla.Patient
和 Hla.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 检测修改,在 DB 中更新并应用于所有实体包括 Patient
.
解决方案是从副本中排除属性 Patient
和 PatientId
:
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
});
Map
在这里似乎用处不大。它不是用来映射的,它是用来在实体中设置单个属性的。这些字段是硬编码的,因此不会有任何收获。问题的代码可以替换为 :
var patient = await _patientRepository.RetrieveEntity(1);
patient.AField1 = "dfgdfg";
await _patientRepository.UnitOfWork.SaveChangesAsync();
对 Update
的调用也没有用,因为 EF 已经跟踪 patient
实体并将检测任何更改。 Update
只需要 untracked/detached 个对象,告诉 EF 开始跟踪处于 Updated
状态的对象。