LINQ多表Join时无需逐一指定属性的实现方案
简化LINQ多表关联中EntityA属性赋值的方法
手动逐个复制EntityA的所有属性确实挺繁琐的,尤其是当属性数量多的时候。这里有几种方法可以帮你避免逐一设置属性,只需要处理那些需要从关联表获取的非映射(NoMap)属性:
方法1:浅拷贝原EntityA对象,仅修改关联属性
利用MemberwiseClone方法快速复制原EntityA的所有属性,然后只更新从关联表拿到的PropB和PropC。注意如果是Entity Framework这类ORM,需要先把查询结果拉到客户端再做拷贝(因为EF无法将MemberwiseClone转换为SQL):
var objectWithJoinedProps = datacontext.EntityA // 先关联EntityB,只保留原EntityA和需要的PropB .Join(dc.EntityB, ta => ta.PropA, tb => tb.PropB, (ta, tb) => new { OriginalA = ta, BProp = tb.PropB }) // 再关联EntityC,保留之前的结果和需要的PropC .Join(dc.EntityC, ab => ab.BProp, tc => tc.PropC, (ab, tc) => new { ab.OriginalA, ab.BProp, CProp = tc.PropC }) // 将数据拉到客户端内存 .AsEnumerable() // 拷贝原对象并赋值关联属性 .Select(data => { var clonedA = data.OriginalA.MemberwiseClone() as EntityA; clonedA.PropB = data.BProp; clonedA.PropC = data.CProp; return clonedA; }) .FirstOrDefault();
MemberwiseClone是浅拷贝,对于值类型属性完全没问题;如果EntityA包含引用类型属性,要注意拷贝后引用会指向同一个对象,不过你的场景里只需要修改NoMap的PropB和PropC,应该不会有问题。
方法2:使用对象映射库(如AutoMapper)
如果你的项目里已经在用AutoMapper,或者属性数量特别多,可以提前配置好EntityA到自身的映射,然后只覆盖需要的关联属性:
首先在项目启动时配置映射:
// 配置EntityA到自身的映射,默认复制所有属性 MapperConfiguration config = new MapperConfiguration(cfg => { cfg.CreateMap<EntityA, EntityA>(); }); IMapper mapper = config.CreateMapper();
然后在LINQ查询中使用:
var objectWithJoinedProps = datacontext.EntityA .Join(dc.EntityB, ta => ta.PropA, tb => tb.PropB, (ta, tb) => new { ta, tb.PropB }) .Join(dc.EntityC, ab => ab.PropB, tc => tc.PropC, (ab, tc) => new { ab.ta, ab.PropB, tc.PropC }) .AsEnumerable() .Select(data => { // 先复制原EntityA的所有属性 var mappedA = mapper.Map<EntityA>(data.ta); // 再赋值关联的NoMap属性 mappedA.PropB = data.PropB; mappedA.PropC = data.CProp; return mappedA; }) .FirstOrDefault();
这种方法的好处是映射配置可以复用,后续如果EntityA新增属性,不需要修改查询代码,只要映射配置没问题就行。
方法3:自定义拷贝方法(适合简单场景)
如果不想用框架,也可以给EntityA写一个自定义的拷贝方法,把所有属性复制逻辑封装起来:
public class EntityA { // 你的现有属性... public int Id { get; set; } public string Prop1 { get; set; } // ...其他属性 [NotMapped] public string PropB { get; set; } [NotMapped] public string PropC { get; set; } // 自定义拷贝方法 public EntityA CloneWithNoMapProps(string propB = null, string propC = null) { return new EntityA { Id = this.Id, Prop1 = this.Prop1, // ...复制所有映射属性 PropB = propB ?? this.PropB, PropC = propC ?? this.PropC }; } }
然后在查询里直接调用:
var objectWithJoinedProps = datacontext.EntityA .Join(dc.EntityB, ta => ta.PropA, tb => tb.PropB, (ta, tb) => new { ta, tb.PropB }) .Join(dc.EntityC, ab => ab.PropB, tc => tc.PropC, (ab, tc) => new { ab.ta, ab.PropB, tc.PropC }) .AsEnumerable() .Select(data => data.ta.CloneWithNoMapProps(data.PropB, data.PropC)) .FirstOrDefault();
这种方法的可控性最强,但如果EntityA属性有变动,需要同步更新拷贝方法。
注意事项
- 如果你的查询是LINQ to Entities(EF),一定要记得用
AsEnumerable()把数据拉到客户端后再做对象拷贝或映射,否则EF无法将这些方法转换为SQL语句,会报错。 - 浅拷贝和AutoMapper的默认映射都是值类型复制,引用类型会共享引用,如果你的EntityA有引用类型属性,需要根据实际情况处理深拷贝。
内容的提问来源于stack exchange,提问作者doorman
相关产品推荐
相关产品推荐

