我正在尝试过滤具有属性(另一个实体,嵌套的)的实体对象。
我想返回作为实体的父级,但是我想用 group 表达式对所有属性进行过滤,因此要进行for循环。
目前它还没有完成,会永远运行吗?
问题是我需要在WHERE内部执行任何操作
IQueryable<Product> entity = _context.Products.AsNoTracking()
.Include(e => e.Attributes)
.ThenInclude(e => e.Attribute.ParentAttribute)
.Include(e => e.Prices);
foreach (var rule in filter.ToRuleGroup().Rules)
{
var compiled = Rule.CompileRule<ProductAttribute>(rule);
entity = entity.Where(x => x.Attributes.Any(y => compiled(y.Attribute)));
}
return entity.Take(10).ToArray();
来自规则类
public static Func<T, bool> CompileRule<T>(Rule r)
{
var paramUser = Expression.Parameter(typeof(T));
Expression expr = BuildExpr<T>(r, paramUser);
// build a lambda function User->bool and compile it
return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
}
public static Expression BuildExpr<T>(Rule r, ParameterExpression param)
{
var left = MemberExpression.Property(param, r.MemberName);
var tProp = typeof(T).GetProperty(r.MemberName).PropertyType;
ExpressionType tBinary;
// is the operator a known .NET operator?
if (ExpressionType.TryParse(r.Operator, out tBinary))
{
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
// use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
return Expression.MakeBinary(tBinary, left, right);
}
else
{
var method = tProp.GetMethod(r.Operator);
var tParam = method.GetParameters()[0].ParameterType;
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
// use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
return Expression.Call(left, method, right);
}
}
编辑Lamda版本:
public static Expression<Func<T, bool>> ToWhere<T>(Rule r)
{
var paramUser = Expression.Parameter(typeof(T));
Expression expr = BuildExpr<T>(r, paramUser);
// build a lambda function User->bool and compile it
return Expression.Lambda<Func<T, bool>>(expr, paramUser);
}
使用任何更改版本编辑2:请参阅@Ivan Stoev的建议
foreach (var rule in filter.ToRuleGroup().Rules){
entity = entity.Where(Rule.Any((Product p) => p.Attributes.Select(a => a.Attribute), rule));
}
return entity;
问题是仍然需要很长时间。我没有得到回应。 这是来自实体的SQL:
SELECT [p].[Id], [p].[IsActive], [p].[IsSyncEnabled], [p].[IsSyncQueued], [p].[LastModifiedDate], [p].[MID], [p].[Sku], [p].[SkuId]
FROM [Product] AS [p]
WHERE (((EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a]
INNER JOIN [Product_Attribute] AS [a.Attribute] ON [a].[AttributeId] = [a.Attribute].[Id]
WHERE ([a.Attribute].[Type] = N'Raw Sku') AND ([p].[Id] = [a].[ProductId])) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a0]
INNER JOIN [Product_Attribute] AS [a.Attribute0] ON [a0].[AttributeId] = [a.Attribute0].[Id]
WHERE ([a.Attribute0].[Name] = N'SMALL') AND ([p].[Id] = [a0].[ProductId]))) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a1]
INNER JOIN [Product_Attribute] AS [a.Attribute1] ON [a1].[AttributeId] = [a.Attribute1].[Id]
WHERE ([a.Attribute1].[Type] = N'Raw Sku') AND ([p].[Id] = [a1].[ProductId]))) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a2]
INNER JOIN [Product_Attribute] AS [a.Attribute2] ON [a2].[AttributeId] = [a.Attribute2].[Id]
WHERE ([a.Attribute2].[Name] = N'LARGE') AND ([p].[Id] = [a2].[ProductId]))) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a3]
INNER JOIN [Product_Attribute] AS [a.Attribute3] ON [a3].[AttributeId] = [a.Attribute3].[Id]
WHERE ([a.Attribute3].[Name] = N'BLACK') AND ([p].[Id] = [a3].[ProductId]))
ORDER BY [p].[Id]
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (93ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [p.Attributes].[Id], [p.Attributes].[AttributeId], [p.Attributes].[ProductId], [p.Attribute].[Id], [p.Attribute].[Hiearchy], [p.Attribute].[Name], [p.Attribute].[ParentAttributeId], [p.Attribute].[Type], [p.Attribute].[Value], [p.Attribute.ParentAttribute].[Id], [p.Attribute.ParentAttribute].[Hiearchy], [p.Attribute.ParentAttribute].[Name], [p.Attribute.ParentAttribute].[ParentAttributeId], [p.Attribute.ParentAttribute].[Type], [p.Attribute.ParentAttribute].[Value]
FROM [Product_ProductAttribute] AS [p.Attributes]
INNER JOIN [Product_Attribute] AS [p.Attribute] ON [p.Attributes].[AttributeId] = [p.Attribute].[Id]
LEFT JOIN [Product_Attribute] AS [p.Attribute.ParentAttribute] ON [p.Attribute].[ParentAttributeId] = [p.Attribute.ParentAttribute].[Id]
INNER JOIN (
SELECT [p0].[Id]
FROM [Product] AS [p0]
WHERE (((EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a4]
INNER JOIN [Product_Attribute] AS [a.Attribute4] ON [a4].[AttributeId] = [a.Attribute4].[Id]
WHERE ([a.Attribute4].[Type] = N'Raw Sku') AND ([p0].[Id] = [a4].[ProductId])) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a5]
INNER JOIN [Product_Attribute] AS [a.Attribute5] ON [a5].[AttributeId] = [a.Attribute5].[Id]
WHERE ([a.Attribute5].[Name] = N'SMALL') AND ([p0].[Id] = [a5].[ProductId]))) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a6]
INNER JOIN [Product_Attribute] AS [a.Attribute6] ON [a6].[AttributeId] = [a.Attribute6].[Id]
WHERE ([a.Attribute6].[Type] = N'Raw Sku') AND ([p0].[Id] = [a6].[ProductId]))) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a7]
INNER JOIN [Product_Attribute] AS [a.Attribute7] ON [a7].[AttributeId] = [a.Attribute7].[Id]
WHERE ([a.Attribute7].[Name] = N'LARGE') AND ([p0].[Id] = [a7].[ProductId]))) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a8]
INNER JOIN [Product_Attribute] AS [a.Attribute8] ON [a8].[AttributeId] = [a.Attribute8].[Id]
WHERE ([a.Attribute8].[Name] = N'BLACK') AND ([p0].[Id] = [a8].[ProductId]))
) AS [t] ON [p.Attributes].[ProductId] = [t].[Id]
ORDER BY [t].[Id]
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (156ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [p.Prices].[Division], [p.Prices].[ProductId], [p.Prices].[PriceId], [p.Prices].[EntryDate], [p.Prices].[EffectiveDate], [p.Prices].[ExpireDate]
FROM [Product_ProductPrice] AS [p.Prices]
INNER JOIN (
SELECT [p1].[Id]
FROM [Product] AS [p1]
WHERE (((EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a9]
INNER JOIN [Product_Attribute] AS [a.Attribute9] ON [a9].[AttributeId] = [a.Attribute9].[Id]
WHERE ([a.Attribute9].[Type] = N'Raw Sku') AND ([p1].[Id] = [a9].[ProductId])) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a10]
INNER JOIN [Product_Attribute] AS [a.Attribute10] ON [a10].[AttributeId] = [a.Attribute10].[Id]
WHERE ([a.Attribute10].[Name] = N'SMALL') AND ([p1].[Id] = [a10].[ProductId]))) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a11]
INNER JOIN [Product_Attribute] AS [a.Attribute11] ON [a11].[AttributeId] = [a.Attribute11].[Id]
WHERE ([a.Attribute11].[Type] = N'Raw Sku') AND ([p1].[Id] = [a11].[ProductId]))) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a12]
INNER JOIN [Product_Attribute] AS [a.Attribute12] ON [a12].[AttributeId] = [a.Attribute12].[Id]
WHERE ([a.Attribute12].[Name] = N'LARGE') AND ([p1].[Id] = [a12].[ProductId]))) AND EXISTS (
SELECT 1
FROM [Product_ProductAttribute] AS [a13]
INNER JOIN [Product_Attribute] AS [a.Attribute13] ON [a13].[AttributeId] = [a.Attribute13].[Id]
WHERE ([a.Attribute13].[Name] = N'BLACK') AND ([p1].[Id] = [a13].[ProductId]))
) AS [t0] ON [p.Prices].[ProductId] = [t0].[Id]
ORDER BY [t0].[Id]
答案 0 :(得分:1)
请勿在查询表达式树中使用委托(Func<...>
),因为它们会导致client evaluation。在您的情况下,应使用Func<T, bool> CompileRule<T>(Rule r)
而不是Expression<Func<T, bool>> ToWhere<T>(Rule r)
(考虑更改名称)。
为了能够将Expression<Func<T, bool>>
用于期望Enumerable.Any<T>
的{{1}}并应用于表达式树内部的内部集合访问器,您需要这样的辅助方法:>
Func<T, bool>
以上是一般实现。 public static partial class ExpressionUtils
{
public static Expression<Func<TOuter, bool>> Any<TOuter, TInner>(Expression<Func<TOuter, IEnumerable<TInner>>> innerSelector, Expression<Func<TInner, bool>> innerPredicate)
{
var parameter = innerSelector.Parameters[0];
var body = Expression.Call(
typeof(Enumerable), nameof(Enumerable.Any), new[] { typeof(TInner) },
innerSelector.Body, innerPredicate);
return Expression.Lambda<Func<TOuter, bool>>(body, parameter);
}
}
类内部的Rule
特定实现将基本上将两者结合起来:
Rule
您的样本的用法为:
public static Expression<Func<TOuter, bool>> Any<TOuter, TInner>(Expression<Func<TOuter, IEnumerable<TInner>>> innerSelector, Rule r)
=> innerSelector.Any(ToWhere<TInner>(r));