Entity Framework中仅在非空int时使用Contains()的查询效率问询
问题:EF中获取待删除Country关联Campaign的最优查询方式
现有实体类定义如下:
public class Country : IOrganization { public int Id { get; private set; } public ICollection<Campaign>? Campaigns { get; private set; } } public class Campaign : IOrganization { public int Id { get; private set; } public State? State { get; private set; } public int? StateId { get; set; } public Country? Country { get; private set; } public int? CountryId { get; set; } }
每个Campaign仅属于State或Country,不会同时属于两者或都不属于。我需要获取即将删除的Country关联的子Campaign集合,当前实现代码如下:
List<Country> deletedCountries = ChangeTracker.Entries<Country>() .Where(e => e.State == EntityState.Deleted) .Select(e => e.Entity).ToList(); List<Campaign> campaignsIndirectDelete = await Campaigns .Where(campaign => campaign.CountryId != null) .Where(campaign => deletedCountries .Select(country => country.Id) .Contains(campaign.CountryId!.Value)) .ToListAsync();
请问这是否是实现该需求的最高效方式?
补充说明:上述代码位于DbContext.SaveChanges()/SaveChangesAsync()中,用于处理EF无法正确处理级联删除的场景,需先删除每个对象的子集合,再删除集合对象本身,这限制了实现方式。
分析与优化建议
你的当前实现功能上是正确的,但并非最优,可从以下两点优化:
避免加载完整的Country实体
当前代码先把待删除的Country实体全部加载到内存再提取Id,会不必要地占用内存(尤其是待删除Country数量较多时)。直接从ChangeTracker中提取Id即可:var deletedCountryIds = ChangeTracker.Entries<Country>() .Where(e => e.State == EntityState.Deleted) .Select(e => e.Entity.Id) .ToList();简化查询条件
因为每个Campaign仅属于State或Country,且Contains查询只会匹配CountryId不为null的记录(deletedCountryIds是int类型集合,null无法匹配其中的值),所以可以去掉Where(campaign => campaign.CountryId != null)这个条件,减少SQL过滤逻辑。
优化后的完整代码:
var deletedCountryIds = ChangeTracker.Entries<Country>() .Where(e => e.State == EntityState.Deleted) .Select(e => e.Entity.Id) .ToList(); var campaignsIndirectDelete = await Campaigns .Where(campaign => deletedCountryIds.Contains(campaign.CountryId!.Value)) .ToListAsync();
额外说明
- 在EF Core 3.0及以上版本中,这种
Contains查询会被自动优化为SQL的IN子句,执行效率很高。 - 由于代码运行在
SaveChangesAsync中,确保ChangeTracker已正确追踪到所有待删除的Country实体即可,无需额外处理上下文状态。
内容的提问来源于stack exchange,提问作者David Thielen
相关产品推荐
相关产品推荐

