如何使用Lambda表达式替代循环处理三层关联DTO的批量提交?
批量提交嵌套关联数据:用Lambda替代foreach实现
问题背景
我有三个类:Header、Property、Units,其中Header与Property为一对多关联,Property与Units为一对多关联。对应的DTO代码如下:
Header DTO
public class HeaderDTO { public string Phone { get; set; } = null!; public string? Source { get; set; } public string? Address { get; set; } public string? Country { get; set; } public string? ShipTo { get; set; } public virtual ICollection<PropertyDTO> Properties { get; set; } = new List<PropertyDTO>(); }
Property DTO
public class PropertyDTO { public string? Floor { get; set; } public string? Area { get; set; } public int Window { get; set; } public string Products { get; set; } = null!; public string? Login { get; set; } public string? ShipTo { get; set; } public int? Flag { get; set; } public virtual ICollection<UnitDTO> Units { get; set; } = new List<UnitDTO>(); }
Units DTO
public class UnitDTO { public string? UniqueName { get; set; } public int Height { get; set; } public int Width { get; set; } public int Panel { get; set; } public int? Belah { get; set; } public string? Img1 { get; set; } public string? Img2 { get; set; } public string? Img3 { get; set; } public List<TDataProduct>? products; }
我需要批量提交Property和Units的JSON记录,目前已通过foreach循环实现功能(如下代码所示),但希望使用Lambda表达式替代循环实现相同逻辑,此前尝试调用Include函数无效,寻求技术指导。
现有接口代码:
[HttpPost("SOPPOST")] public async Task<ActionResult<TDataHeader>> CreateHeaders(HeaderDTO hdto) { var header = new HeaderDTO { Address = hdto.Address, Country = hdto.Country, Phone = hdto.Phone, ShipTo = hdto.ShipTo, Source = hdto.Source }; var phoneVerify = _context.TDataHeaders.Count(x => x.Phone == header.Phone); var headerImport = new TDataHeader { Address = header.Address, Country = header.Country, Phone = header.Phone, ShipTo = header.ShipTo, Source = header.Source }; //if (phoneVerify == 0) //{ // await _context.TDataHeaders.AddAsync(headerImport); //} foreach (var prop in hdto.Properties) { var property = new TDataProperty { Floor = prop.Floor, Area = prop.Area, Windows = prop.Windows, Flag = prop.Flag, Login = prop.Login, Products = prop.Products, ShipTo = prop.ShipTo, Header = headerImport }; foreach (var unit in prop.Units) { var unitModel = new TDataUnit { Belah = unit.Belah, Height = unit.Height, Width = unit.Width, Panel = unit.Panel, Img1 = unit.Img1, Img2 = unit.Img2, Img3 = unit.Img3, UniqueName = prop.Floor + "-" + prop.Area + "-" + prop.Windows, Property = property }; await _context.TDataUnits.AddRangeAsync(unitModel); } await _context.TDataProperties.AddRangeAsync(property); } await _context.SaveChangesAsync(); return Ok("Posted"); }
解决方案
首先明确:Include是Entity Framework中查询阶段加载关联实体的方法,和数据插入逻辑完全无关,所以这里用它解决批量插入问题是找错了方向。
要通过Lambda表达式(结合LINQ的Select、SelectMany)替代foreach循环,核心思路是将DTO集合转换为实体集合,再批量添加到上下文。
修改后的接口代码如下:
[HttpPost("SOPPOST")] public async Task<ActionResult<TDataHeader>> CreateHeaders(HeaderDTO hdto) { var headerImport = new TDataHeader { Address = hdto.Address, Country = hdto.Country, Phone = hdto.Phone, ShipTo = hdto.ShipTo, Source = hdto.Source }; // 验证手机号是否存在(原逻辑保留) var phoneExists = await _context.TDataHeaders.AnyAsync(x => x.Phone == headerImport.Phone); //if (!phoneExists) //{ // await _context.TDataHeaders.AddAsync(headerImport); //} // 将DTO转换为Property实体集合,同时生成对应的Unit实体 var propertyEntities = hdto.Properties.Select(prop => new TDataProperty { Floor = prop.Floor, Area = prop.Area, Windows = prop.Window, // 注意原DTO中是Window,实体是Windows,需匹配字段名 Flag = prop.Flag, Login = prop.Login, Products = prop.Products, ShipTo = prop.ShipTo, Header = headerImport, // 直接生成关联的Unit实体集合 Units = prop.Units.Select(unit => new TDataUnit { Belah = unit.Belah, Height = unit.Height, Width = unit.Width, Panel = unit.Panel, Img1 = unit.Img1, Img2 = unit.Img2, Img3 = unit.Img3, UniqueName = $"{prop.Floor}-{prop.Area}-{prop.Window}", Property = property // 关联到当前创建的Property实体 }).ToList() }).ToList(); // 批量添加Property,EF会自动处理关联的Unit实体,无需单独添加 await _context.TDataProperties.AddRangeAsync(propertyEntities); await _context.SaveChangesAsync(); return Ok("Posted"); }
关键说明
- LINQ转换逻辑:用
Select将每个PropertyDTO转换为TDataProperty,内部嵌套Select完成UnitDTO到TDataUnit的转换并建立关联。 - 批量操作效率:一次性添加整个Property集合,相比原代码多次调用
AddRangeAsync更高效,EF会自动追踪并插入关联的Unit实体。 - 字段匹配注意:原DTO中PropertyDTO的字段是
Window(单数),实体TDataProperty是Windows(复数),转换时需确保字段名一致,避免赋值错误。 - Include的正确用法:
Include仅用于查询场景加载关联数据,插入/更新时依靠EF对导航属性的自动追踪即可,无需调用该方法。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

