如何高效编写C# Lambda查询?简化三步Lambda查询流程
如何简化多步Lambda查询并优化C# EF查询效率?
我现在需要通过三步Lambda查询才能得到想要的结果:第一步查询归属指定someone的Target;第二步从关联表ProjectTarget中获取这些Target对应的ProjectId;第三步通过ProjectId查询所有Project。我觉得应该有更高效的实现方式,但暂时没找到。相关代码如下:
public async Task<List<Project>> GetProjectsFromSomeone(string someone) { var targetIds = from target in Context.Targets where target.Someone.ToLower().Contains(someone.ToLower()) select target.Id; var projectIds = from pt in Context.ProjectTargets where targetIds.Any(id => id == pt.TargetId) select pt.ProjectId; var projects = from prj in Context.Projects where projectIds.Any(id => id == prj.Id) select prj; return await projects.ToListAsync(); } public class ProjectTarget { public int ProjectId { get; set; } public int TargetId { get; set; } public Project Project { get; set; } public Target Target { get; set; } } public class Target { public int Id { get; set; } public string Code { get; set; } public string Someone { get; set; } } public class Project { public int Id { get; set; } public string Code { get; set; } public string Name { get; set; } }
同时想请教下,如何更高效地编写C# Lambda查询?
简化你的查询方案
没问题,你的三步查询完全可以合并成一步,既简化代码,又能让EF生成更高效的SQL(避免多次子查询带来的性能损耗)。这里有两种很实用的优化方式:
方式1:使用Linq查询语法(链式关联)
把三次查询通过JOIN合并,直接从Project关联到中间表再关联到Target,一步筛选出符合条件的项目:
public async Task<List<Project>> GetProjectsFromSomeone(string someone) { // 提前处理输入的小写转换,避免在查询条件中重复执行 var lowerSomeone = someone.ToLower(); var projects = from prj in Context.Projects join pt in Context.ProjectTargets on prj.Id equals pt.ProjectId join target in Context.Targets on pt.TargetId equals target.Id where target.Someone.ToLower().Contains(lowerSomeone) select prj; // 去重:一个项目可能对应多个Target,避免返回重复实例 return await projects.Distinct().ToListAsync(); }
方式2:使用Lambda方法语法(利用导航属性)
你的ProjectTarget已经定义了Project和Target的导航属性,直接通过导航属性关联会更简洁,EF也能自动生成优化的SQL:
public async Task<List<Project>> GetProjectsFromSomeone(string someone) { var lowerSomeone = someone.ToLower(); var projects = Context.ProjectTargets .Where(pt => pt.Target.Someone.ToLower().Contains(lowerSomeone)) .Select(pt => pt.Project) .Distinct(); return await projects.ToListAsync(); }
小贴士:如果你的EF上下文没开启延迟加载,或者需要确保导航属性被正确加载,可以加上
.Include(pt => pt.Target).Include(pt => pt.Project),不过大多数情况下EF会自动处理关联查询的SQL生成,不需要手动Include。
高效编写C# Lambda查询的实用技巧
- 提前处理常量值:像上面的
lowerSomeone,提前把输入转成小写,避免在查询条件中重复执行转换操作,EF也能更好地缓存查询计划。 - 优先用导航属性代替手动JOIN:利用实体类的导航属性(比如
ProjectTarget.Target),代码更简洁,EF生成的SQL也更贴合数据库的优化逻辑。 - 避免多层子查询:原始代码里的两次
Any子查询会生成嵌套SQL,数据库优化器处理起来不如JOIN高效,合并成关联查询能显著提升性能。 - 按需去重:如果存在一对多的关联(比如一个Project对应多个Target),记得用
Distinct()避免返回重复的实体实例。 - 坚持使用异步查询方法:用
ToListAsync()、FirstOrDefaultAsync()这类异步方法,避免阻塞线程,提升应用的并发处理能力。 - 不要在内存中过滤数据:绝对不要先调用
ToList()把所有数据拉到内存再筛选,这样会导致大量不必要的数据加载,性能急剧下降——尽量让所有过滤条件在数据库层面执行。
内容的提问来源于stack exchange,提问作者T0mba
相关产品推荐
相关产品推荐

