EF Core 如何使用另一数据库查询得到的列表过滤数据?
可以先从 list1 中提取所有 Operations.Name,组成字符串列表,再用 Contains 过滤 dbcontext2.ProdDetails。过滤条件应写在第二次查询的 ToListAsync() 之前,让数据库筛选后再返回结果。
这种做法不需要 linked server。应用先从第一个数据库取得匹配值,再用这些值作为第二次查询的条件。能否翻译以及生成什么 SQL,取决于 EF Core 版本和数据库 provider。
为什么嵌套 Any 可能无法执行?
list1 已经通过 ToListAsync() 加载到内存。原表达式要求 EF Core 将内存实体集合及其多层导航属性上的 Any,与第二个数据库的查询一起翻译成 SQL。
这类表达式不一定受支持。原问题没有提供异常信息,无法确定此次失败的具体原因。将条件简化为“数据库字段是否存在于一个标量值列表中”,通常更适合查询翻译。
从现有 list1 提取名称并过滤
保留第一次查询,在它之后提取名称,再修改第二次查询:
var operationNames = list1
.SelectMany(area => area.Subareas)
.SelectMany(subarea => subarea.Operations)
.Select(operation => operation.Name)
.Where(name => !string.IsNullOrEmpty(name))
.Distinct()
.ToList();
var list2 = await dbcontext2.ProdDetails
.Where(detail => !string.IsNullOrEmpty(detail.OperationName))
.Where(detail => operationNames.Contains(detail.OperationName))
.Select(detail => new
{
Customer = detail.Customer,
Assembly = detail.Assembly,
AssemblyDescription = detail.AssemblyDescription,
AssemblyRevision = detail.AssemblyRevision,
WorkOrder = detail.WorkOrder,
Batch = detail.Batch,
BatchQuantity = detail.BatchQuantity,
Barcode = detail.Barcode,
LastWipTime = detail.LastWipTime,
InProcess = detail.InProcess,
OperationName = detail.OperationName
})
.ToListAsync();
SelectMany 将多层集合展开为 Operation 集合,Distinct() 去掉重复名称。这段代码沿用原有 Include 和 ThenInclude 已加载导航集合的前提。
operationNames.Contains(detail.OperationName) 表示检查名称是否在集合中。对于支持该翻译的 provider,EF Core 会将其转换为数据库端的筛选条件。具体实现可能是 IN 或其他集合查询形式,不能假定所有版本都生成相同的 SQL。
如果只需要名称,可以简化第一次查询
如果后续逻辑不需要完整的 Areas、Subareas 和 Operations 实体,可以直接查询名称,省去加载整棵实体关系的开销:
var operationNames = await dbcontext1.Areas
.Where(area => area.Name == selectedArea!.Name)
.SelectMany(area => area.Subareas)
.SelectMany(subarea => subarea.Operations)
.Where(operation => !string.IsNullOrEmpty(operation.Name))
.Select(operation => operation.Name)
.Distinct()
.ToListAsync();
之后使用相同的 Contains 条件查询 ProdDetails。名称已经通过投影取得,无须再添加 Include。
两个数据库之间仍需要一次应用端的数据传递。不要将第一个 context 尚未执行的 IQueryable 直接嵌入第二个 context 的查询;应先通过 ToListAsync() 取得名称列表。
数据量和匹配规则需要注意什么?
- 不要提前加载 ProdDetails。如果先调用
ToListAsync()或切换到AsEnumerable(),再执行过滤,就无法减少第二个数据库返回的数据量。 - 名称列表为空时,可以直接结束本次匹配。此时没有符合条件的记录,应按页面逻辑返回或清空结果,避免执行第二次查询。
- 名称很多时,
Contains也可能成为瓶颈。集合参数的处理方式、参数数量限制和查询开销取决于 provider 与版本。如果列表很大,可以结合实际数据库考虑分批查询,或将名称写入第二个数据库可查询的临时表、暂存表后关联。这些方案需要相应的数据库支持和权限。 - 字符串比较遵循第二个数据库的规则。大小写、排序规则以及空格的处理可能与内存中的 C# 字符串比较不同。如果结果不符合预期,应检查两端的名称数据和数据库比较规则。
- 筛选后的结果仍可能很大。如果页面只展示其中一部分,应根据业务添加稳定排序和分页,并检查
OperationName相关索引是否适合该查询。
另外,原示例的方法声明缺少完整括号,应写为 private async Task GetRecords()。这是独立的语法问题。如果修改筛选后仍出现查询翻译异常,需要结合实际异常、EF Core 版本和 provider 进一步定位。
备注:内容仅供参考。
