EF Core 无法翻译表达式来比较两个集合,而 EF 6 可以
EF Core can't translate an expression to compare two collections which EF 6 could
我在旧 Entity Framework (.NET Framework) 中有以下查询:
db.ProductVariations
.Where(pv => pv.Product.Categories
.Any(cat => categorySearchStrings
.Any(categorySearchString => cat.SearchTree.StartsWith(categorySearchString))));
我意识到这不太好,但我正在重构一个遗留应用程序,我们必须选择我们的战斗。
所以你可以传递一个搜索字符串列表(categorySearchStrings
),例如:
"38.54.", "45."
这基本上是 search tree 的一个实现,其中我们数据库中的每个类别都有一个 SearchTree
属性。因此具有搜索树 38.54.99
的类别会匹配,但 38.
不会匹配。
一个产品可以有多个类别,我们可以将多个搜索树字符串传递给查询。所以我们正在比较两个集合。
这被翻译成
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[ProductVariation] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM ( SELECT
[Extent3].[SearchTree] AS [SearchTree]
FROM [dbo].[ProductCategory] AS [Extent2]
INNER JOIN [dbo].[Category] AS [Extent3] ON [Extent2].[CategoryId] = [Extent3].[Id]
WHERE [Extent1].[ProductId] = [Extent2].[ProductId]
) AS [Project1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
WHERE ( CAST(CHARINDEX(N'38.', [Project1].[SearchTree]) AS int)) = 1
)
)
) AS [GroupBy1]
我正在尝试迁移到 Entity Framework Core(6,.NET 6 上的 运行),但现在出现以下错误:
System.InvalidOperationException : The LINQ expression 'categorySearchString => categorySearchString == "" || EntityShaperExpression:
Company.Data.Models.Category
ValueBufferExpression:
ProjectionBindingExpression: Inner
IsNullable: False
.SearchTree != null && categorySearchString != null && EntityShaperExpression:
Company.Data.Models.Category
ValueBufferExpression:
ProjectionBindingExpression: Inner
IsNullable: False
.SearchTree.StartsWith(categorySearchString)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
我认为切换到客户评估并不是一个真正的选择,因为要检索的数据太多了。另外,除了这个 Where 子句之外,还有更多的事情要做。我简化了。
我也试过改写成这样:
.Where(pv => pv.Product.Categories.Select(c => c.SearchTree).Any(st => categorySearchStrings.Any(ss => st.StartsWith(ss))));
但是我得到了同样的错误。
是否可以使用 EF Core 执行此操作?
我倾向于构建一个动态 expression tree 来表示过滤器:
var cat = Expression.Parameter(typeof(Category), "cat");
var parts = new List<Expression>(categorySearchStrings.Count);
var startsWithMethod = typeof(string).GetMethod(nameof(string.StartsWith), new[] { typeof(string) });
foreach (string categorySearchString in categorySearchStrings)
{
var searchTree = Expression.Property(cat, nameof(Category.SearchTree));
var value = Expression.Constant(categorySearchString);
var startsWith = Expression.Call(searchTree, startsWithMethod, value);
parts.Add(startsWith);
}
var body = parts.Aggregate(Expression.OrElse);
var categoryFilter = Expression.Lambda<Func<Category, bool>>(body, cat);
var pv = Expression.Parameter(typeof(ProductVariation), "pv");
var product = Expression.Property(pv, nameof(ProductVariation.Product));
var categories = Expression.Property(product, nameof(Product.Categories));
var any = Expression.Call(typeof(Enumerable), nameof(Enumerable.Any), new[] { typeof(Category) }, categories, categoryFilter);
var finalFilter = Expression.Lambda<Func<ProductVariation, bool>>(any, pv);
db.ProductVariations
.Where(finalFilter)
...
您还应该将此报告为 an issue on the efcore repository,以查看它是否可以在未来的版本中修复。
更新:问题是 created but was a duplicate of an existing issue。
我在旧 Entity Framework (.NET Framework) 中有以下查询:
db.ProductVariations
.Where(pv => pv.Product.Categories
.Any(cat => categorySearchStrings
.Any(categorySearchString => cat.SearchTree.StartsWith(categorySearchString))));
我意识到这不太好,但我正在重构一个遗留应用程序,我们必须选择我们的战斗。
所以你可以传递一个搜索字符串列表(categorySearchStrings
),例如:
"38.54.", "45."
这基本上是 search tree 的一个实现,其中我们数据库中的每个类别都有一个 SearchTree
属性。因此具有搜索树 38.54.99
的类别会匹配,但 38.
不会匹配。
一个产品可以有多个类别,我们可以将多个搜索树字符串传递给查询。所以我们正在比较两个集合。
这被翻译成
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[ProductVariation] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM ( SELECT
[Extent3].[SearchTree] AS [SearchTree]
FROM [dbo].[ProductCategory] AS [Extent2]
INNER JOIN [dbo].[Category] AS [Extent3] ON [Extent2].[CategoryId] = [Extent3].[Id]
WHERE [Extent1].[ProductId] = [Extent2].[ProductId]
) AS [Project1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
WHERE ( CAST(CHARINDEX(N'38.', [Project1].[SearchTree]) AS int)) = 1
)
)
) AS [GroupBy1]
我正在尝试迁移到 Entity Framework Core(6,.NET 6 上的 运行),但现在出现以下错误:
System.InvalidOperationException : The LINQ expression 'categorySearchString => categorySearchString == "" || EntityShaperExpression:
Company.Data.Models.Category
ValueBufferExpression:
ProjectionBindingExpression: Inner
IsNullable: False
.SearchTree != null && categorySearchString != null && EntityShaperExpression:
Company.Data.Models.Category
ValueBufferExpression:
ProjectionBindingExpression: Inner
IsNullable: False
.SearchTree.StartsWith(categorySearchString)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
我认为切换到客户评估并不是一个真正的选择,因为要检索的数据太多了。另外,除了这个 Where 子句之外,还有更多的事情要做。我简化了。
我也试过改写成这样:
.Where(pv => pv.Product.Categories.Select(c => c.SearchTree).Any(st => categorySearchStrings.Any(ss => st.StartsWith(ss))));
但是我得到了同样的错误。
是否可以使用 EF Core 执行此操作?
我倾向于构建一个动态 expression tree 来表示过滤器:
var cat = Expression.Parameter(typeof(Category), "cat");
var parts = new List<Expression>(categorySearchStrings.Count);
var startsWithMethod = typeof(string).GetMethod(nameof(string.StartsWith), new[] { typeof(string) });
foreach (string categorySearchString in categorySearchStrings)
{
var searchTree = Expression.Property(cat, nameof(Category.SearchTree));
var value = Expression.Constant(categorySearchString);
var startsWith = Expression.Call(searchTree, startsWithMethod, value);
parts.Add(startsWith);
}
var body = parts.Aggregate(Expression.OrElse);
var categoryFilter = Expression.Lambda<Func<Category, bool>>(body, cat);
var pv = Expression.Parameter(typeof(ProductVariation), "pv");
var product = Expression.Property(pv, nameof(ProductVariation.Product));
var categories = Expression.Property(product, nameof(Product.Categories));
var any = Expression.Call(typeof(Enumerable), nameof(Enumerable.Any), new[] { typeof(Category) }, categories, categoryFilter);
var finalFilter = Expression.Lambda<Func<ProductVariation, bool>>(any, pv);
db.ProductVariations
.Where(finalFilter)
...
您还应该将此报告为 an issue on the efcore repository,以查看它是否可以在未来的版本中修复。
更新:问题是 created but was a duplicate of an existing issue。