使用 Intersect 到 IQueryable 和 EfCore

use Intersect into IQueryable and EfCore

我正在尝试将 LinQ Intersect(或等效方法)用于 IQueryable 方法,但似乎我做错了。

我有一些产品符合某些规格(如颜色、材料、高度...),这些规格具有不同的值,例如:

颜色:蓝色、红色、黄色 身高 : 128cm, 152cm...

我需要获取匹配 ALL 我提供的夫妇 specifityId / specifityValue 列表的产品。

这是我正在尝试做的事情:

// The list of couple SpecifityID (color, material..) / SpecifityValue (red, yellow, wood...)
List<string> SpecId_SpecValue = new List<string>();

SpecId_SpecValue.Add("3535a444-1139-4a1e-989f-795eb9be43be_BEA");
SpecId_SpecValue.Add("35ad6162-a885-4a6a-8044-78b68f6b2c4b_Purple");

int filterCOunt = SpecId_SpecValue.Count;

var query =
            Products
                    .Include(pd => pd.ProductsSpecifity)
                .Where(z => SpecId_SpecValue
                    .Intersect(z.ProductsSpecifity.Select(x => (x.SpecifityID.ToString() + "_" + x.SpecifityValue)).ToList()).Count() == filterCOunt);

我收到错误:InvalidOperationException:无法翻译 LINQ 表达式“DbSet()”。以可以翻译的形式重写查询,或者通过插入对 'AsEnumerable'、'AsAsyncEnumerable'、'ToList' 或 'ToListAsync' 的调用显式切换到客户端评估。有关详细信息,请参阅 https://go.microsoft.com/fwlink/?linkid=2101038 这意味着它无法转换为 SQL,我需要在过滤器之前使用 ToList。

问题是,我不想调用 ToList(),因为我的数据库中有大量产品,我不想在过滤之前将它们加载到内存中。

还有其他方法可以实现我需要做的事情吗?

查询表示“产品的所有 x.SpecifityID.ToString() + "_" + x.SpecifityValue 组合与某些给定组合完全匹配”。

Except 这样的集合组合运算符通常不能很好地与 EF 配合使用,原因有很多,我不会在这里讨论。幸运的是,在许多这些情况下,可以使用 Contains 找到 work-around,EF 确实很好地支持它。你的情况:

var query = Products.Include(pd => pd.ProductsSpecifity)
    .Where(z => z.ProductsSpecifity
        .Select(x => x.SpecifityID.ToString() + "_" + x.SpecifityValue)
            .Count(s => SpecId_SpecValue.Contains(s)) == filterCount);

请注意比较效率不高。在比较之前转换数据库值会禁用索引的任何使用(不是 sargable)。但是在 EF 中更有效地执行此操作并非易事,请参阅 this.

我最终使用了在 link @Gert Arnold 提供的 here.

中找到的解决方案

我使用了BlazarTech.QueryableValues.SqlServer @yv989c 的回答

这是现在非常有效的方法:

// The list of couple SpecifityID (color, material..) / SpecifityValue (red, yellow, wood...)
            Dictionary<Guid, string> SpecId_SpecValue = new Dictionary<Guid, string>();

            SpecId_SpecValue.Add(new Guid("3535a444-1139-4a1e-989f-795eb9be43be"), "BEA");
            SpecId_SpecValue.Add(new Guid("35ad6162-a885-4a6a-8044-78b68f6b2c4b"), "Purple");


            // BlazarTech.QueryableValues.SqlServer
            var queryableValues = DbContext.AsQueryableValues(SpecId_SpecValue);

            var query = Products.Include(pd => pd.ProductsSpecifity)
                .Where(x => x.ProductsSpecifity
                    .Where(e => queryableValues
                        .Where(v =>
                            v.Key == e.SpecifityID &&
                            v.Value == e.SpecifityValue
                        )
                        .Any()
                    ).Count() == dynamicFilter.Count);