C# EF7如何处理嵌套实体的更新操作
我试图找出更新实体及其所有子实体时的最佳实践。例如我有一个“雇主”更新服务,可以更新雇主实体和雇主的“地址”实体以及每个“地址”的“电话”实体。用户可以向现有雇主添加新地址,或者更新当前地址,或者删除一些地址,这同样适用于每个地址的电话。你能帮我写一个理想的代码来处理这个场景吗 我使用的是EF7 rc1,我使用Automapper将Dto映射到我服务中的实体C# EF7如何处理嵌套实体的更新操作,c#,entity-framework,asp.net-core,entity-framework-core,C#,Entity Framework,Asp.net Core,Entity Framework Core,我试图找出更新实体及其所有子实体时的最佳实践。例如我有一个“雇主”更新服务,可以更新雇主实体和雇主的“地址”实体以及每个“地址”的“电话”实体。用户可以向现有雇主添加新地址,或者更新当前地址,或者删除一些地址,这同样适用于每个地址的电话。你能帮我写一个理想的代码来处理这个场景吗 我使用的是EF7 rc1,我使用Automapper将Dto映射到我服务中的实体 public partial class Employer { public int EmployerId { get; set;
public partial class Employer
{
public int EmployerId { get; set; }
public int Name { get; set; }
[InverseProperty("Employer")]
public virtual ICollection<Address> Address { get; set; }
}
public partial class Address
{
public int AddressId { get; set; }
public int Address1{ get; set; }
public int City { get; set; }
[ForeignKey("EmployerId")]
[InverseProperty("Address")]
public virtual Employer Employer { get; set; }
[InverseProperty("Address")]
public virtual ICollection<Phone> Phone { get; set; }
}
public partial class Phone
{
public int PhoneId { get; set; }
public string Number { get; set; }
[ForeignKey("AddressId")]
[InverseProperty("Phone")]
public virtual Address Address { get; set; }
}
公共部分类雇主
{
public int EmployerId{get;set;}
公共int名称{get;set;}
[反向财产(“雇主”)]
公共虚拟ICollection地址{get;set;}
}
公共部分类地址
{
public int AddressId{get;set;}
公共int地址1{get;set;}
公共int城市{get;set;}
[外键(“雇员ID”)]
[反向属性(“地址”)]
公共虚拟雇主{get;set;}
[反向属性(“地址”)]
公共虚拟ICollection电话{get;set;}
}
公共半类电话
{
公共int PhoneId{get;set;}
公共字符串编号{get;set;}
[外键(“地址ID”)]
[反向属性(“电话”)]
公共虚拟地址{get;set;}
}
我的服务方式
public async Task<IServiceResult> Update(EmployerDto employer)
{
var employerDbEntity = await _db.Employer
.Include(a=>a.Address).ThenInclude(p=>p.Phone)
.SingleOrDefaultAsync (a=>a.EmployerId == employer.EmployerId);
//How to handle the update operation for children?
var entity = Mapper.Map<Employer>(employer);
HandleChildren(employerDbEntity,entity);
await _db.SaveChangesAsync();
...
...
}
private void HandleChildren(Employer employerDbEntity,Employer entity)
{
//Delete
foreach (var existing in employerDbEntity.Address.ToList())
{
if (!entity.Address.Any(a => a.AddressId == existing.AddressId))
employerDbEntity.Address.Remove(existing);
}
//Update or Insert
foreach (var address in entity.Address)
{
var existing = employerDbEntity.Address.SingleOrDefault(a =>a.AddressId == address.AddressId);
//Insert
if (existing == null)
{
employerDbEntity.Address.Add(address);
}
//Update
else
{
Mapper.Map(address, existing);
}
}
}
公共异步任务更新(雇主到雇主)
{
var EmployerBentity=等待雇主
.Include(a=>a.Address)。然后Include(p=>p.Phone)
.SingleOrDefaultAsync(a=>a.EmployerId==employer.EmployerId);
//如何处理孩子的更新操作?
var实体=映射器.Map(雇主);
手把手儿童(雇主、实体);
等待_db.SaveChangesAsync();
...
...
}
私人无效手把手儿童(雇主、雇员、雇主实体)
{
//删除
foreach(EmployerBenty.Address.ToList()中存在的变量)
{
如果(!entity.Address.Any(a=>a.AddressId==existing.AddressId))
EmployerBenty.Address.Remove(现有);
}
//更新或插入
foreach(entity.address中的var地址)
{
var existing=EmployerBenty.Address.SingleOrDefault(a=>a.AddressId==Address.AddressId);
//插入
if(现有==null)
{
EmployerBenty.Address.Add(地址);
}
//更新
其他的
{
地图(地址,现有);
}
}
}
此示例看起来是处理子集合的一种不错的方法。必须手动检查每个集合执行的操作。(使用泛型听起来不错,但总是会在某种程度上影响到性能。)
考虑到这一点,以下是一些建议:
- 将子集合处理移动到单独的方法/服务中李>
- 如果查询现有实体,则在一个查询中检索整个集合,然后在内存中迭代结果
- 因为您正在编写异步代码,所以可以利用并行处理子集合的优势!为此,每个操作都应该创建自己的上下文李>
private async Task UpdateAddresses(List<Address> addressesToUpdate)
{
using(var context = new Context())
{
var existingAddressIds = await context.Addresses
.Where(a => addressesToUpdate.Contains(a.AddressId))
.ToListAsync()
.ConfigureAwait(false);
existingAddressIds.ForEach(a => context.Addresses.Remove(a));
await context.SaveChangesAsync().ConfigureAwait(false);
}
}
专用异步任务更新地址(列表地址更新)
{
使用(var context=new context())
{
var existingAddressIds=await context.Addresses
.Where(a=>addressesToUpdate.Contains(a.AddressId))
.ToListAsync()
.配置等待(错误);
existingAddressIds.ForEach(a=>context.Addresses.Remove(a));
wait context.saveChangesSync().ConfigureWait(false);
}
}