Entity Framework Core Include用法选型:三种方案对比咨询
最优方案分析:EF Core 获取带关联数据的实体
从EF 6转到EF Core,刚接触Include的话先明确核心:Include是EF Core里用于急加载关联实体的方法(和EF6用法类似,但EF Core的查询优化逻辑更严谨)。直接给结论:Option 2是最优、最快的方案,下面逐个拆解三个选项的问题和优势:
逐个选项分析
Option 1:存在多余的数据库查询
using (var context = new TrainingSchedulerContext()) { var clubs = context.Clubs.Where(c => c.Id == clubId); if (clubs.Count() == 0) return NotFound(); var club = clubs.Include(c => c.Memberships) .Include(c => c.MembershipRequests) .Include(c => c.TrainingTimes) .Include(c => c.AdminRoles) .SingleOrDefault(); }
这个方案的核心问题是触发了两次数据库查询:
- 调用
clubs.Count()时,EF会发送一条SELECT COUNT(*) FROM Clubs WHERE Id = @clubId的SQL到数据库; - 后续的
Include(...).SingleOrDefault()又会发送一条包含多表JOIN的查询,获取带关联数据的俱乐部。
其实完全没必要先查Count——SingleOrDefault本身就会在找不到匹配时返回null,直接判断club == null即可,多一次查询只会增加数据库往返开销,拖慢性能。
Option 2:最优且高效的方案
using (var context = new TrainingSchedulerContext()) { var club = context.Clubs.Include(c => c.Memberships) .Include(c => c.MembershipRequests) .Include(c => c.TrainingTimes) .Include(c => c.AdminRoles) .SingleOrDefault(c => c.Id == clubId); if (club == null) return NotFound(); }
这个方案完美贴合EF Core的最佳实践:
- 仅触发一次数据库查询:EF会生成一条包含所有关联表JOIN的SQL,一次性获取俱乐部及其所有需要的关联数据,避免了多次数据库往返的性能损耗;
- 逻辑简洁直观:直接把过滤条件(
Id == clubId)放在SingleOrDefault中,同时通过Include指定要加载的关联实体,代码清晰易读; - 语义准确:
SingleOrDefault针对主键查询(Id是唯一主键),既保证找到唯一匹配的实体,又能在无匹配时返回null,直接判断即可返回NotFound。
Option 3:完全冗余的两次查询
using (var context = new TrainingSchedulerContext()) { var club = context.Clubs.SingleOrDefault(c => c.Id == clubId); if (club == null) return NotFound(); club = context.Clubs.Include(c => c.Memberships) .Include(c => c.MembershipRequests) .Include(c => c.TrainingTimes) .Include(c => c.AdminRoles) .SingleOrDefault(c => c.Id == clubId); }
这个方案的问题最突出:做了两次完全不必要的数据库查询:
- 第一次查询获取了不带任何关联数据的俱乐部实体,仅仅用来判断是否存在;
- 第二次又重新查询一次带关联数据的实体,完全浪费了第一次查询的资源和时间。
不仅性能最差,代码也冗余,完全没有必要这么写。
额外补充(针对EF 6转EF Core的注意点)
- EF Core默认关闭延迟加载(EF6默认开启),所以如果不用
Include,关联实体(比如Memberships)会是null或者空集合(取决于配置),这和EF6的行为不同,必须显式用Include来加载需要的关联数据; - 如果关联实体还有下级关联(比如
Memberships里的User),可以用ThenInclude链式加载,比如Include(c => c.Memberships).ThenInclude(m => m.User); - 针对主键查询,用
SingleOrDefault比FirstOrDefault更合适,它明确表达了“主键唯一,最多一个匹配”的语义,如果意外出现多个匹配(比如主键重复,业务上几乎不可能),会抛出异常,能更早发现问题。
内容的提问来源于stack exchange,提问作者S. Doe
相关产品推荐
相关产品推荐

