构建表达式以在对象的子属性(集合)中搜索
Building Expression to search in child properies (collection) of object
我有一个搜索设置,可以根据条件 return 匹配 Record
对象。它根据条件类型构建表达式树,并查询数据库以匹配 return。以前,它只匹配顶级属性,但现在我试图在 Record
的子属性中搜索并构建一个可以在查询中与其他属性组合的表达式。对象看起来像这样:
public class Record
{
public virtual ICollection<Attribute> Attributes { get; set; }
}
public class Attribute
{
public Guid AttributeId { get; set; }
public string Value { get; set; }
public virtual Record Record { get; set; }
}
可能的条件之一是 AttributeId
和 Value
的值对,为此我会 return 所有 Record
具有 [=18= 的行] 匹配两个值。例如,如果我只是编写一个简单的 LINQ 查询,它将如下所示:
Guid searchId = Guid.NewGuid();
string searchValue = "asdf";
IQueryable<Record> records = GetSomeQueryToRecords();
IEnumerable<Record> recordsThatMatch = records
.Where(r => r.Attribute.Any(a => a.AttributeId == searchId && a.Value == searchValue));
我需要将其构建到我的表达式树中,以便它在内存中和 EF Core 查询中都能工作。由 GetExpression
编辑的表达式 return 最终将与其他使用 AndAlso
的表达式组合,以构建一个大型查询。我试过这个:
ParameterExpression record = Expression.Parameter(typeof(Record));
Expression searchExpression = GetExpression(record, searchId, searchValue);
Expression<Func<Record, bool>> lambda = Expression.Lamda<Func<Record, bool>>(searchExpression, record);
recordsThatMatch = records.Where(lambda);
private Expression GetExpression(ParameterExpression parameter, Guid searchId, string searchValue)
{
// Get the Record.Attributes Memboer.
MemberExpression attributes = Expression.Property(parameter, nameof(Record.Attributes));
// Get the "record.Attributes.Any(predicate)" Method.
MethodInfo anyMethod = typeof(Enumerable).GetMethods()
.Where(m => m.Name == nameof(Enumerable.Any))
.Where(m => m.GetParameters().Length == 2)
.Single();
// Make Method generic.
anyMethod = anyMethod.MakeGenericMethod(typeof(Attribute));
ParameterExpression attribute = Expression.Parameter(typeof(Attribute));
Expression idEqualExpression = Expression.Equal(
Expression.Property(attribute, nameof(Attribute.AttributeId)),
Expression.Constant(searchId)
);
Expression valueEqualExpression = Expression.Equal(
Expression.Property(attribute, nameof(Attribute.Value)),
Expression.Constant(searchValue)
);
Expression attributeMatchExpression = Expression.AndAlso(idEqualExpression, valueEqualExpression);
return Expression.Call(null, anyMethod, attributes, attributeMatchExpression);
}
不过我得到了 ArgumentException
和 Expression.Call
,因为老实说我不知道如何将它用于这个特定实例,而且它有很多覆盖。我相信我应该传递 null
因为我实际上想要一个静态方法,因为 .Any()
是一个扩展方法。
您忘记创建 'LambdaExpresson'。还有Expression.Call
可以简化。
private Expression GetExpression(ParameterExpression parameter, Guid searchId, string searchValue)
{
// Get the Record.Attributes Memboer.
MemberExpression attributes = Expression.Property(parameter, nameof(Record.Attributes));
ParameterExpression attribute = Expression.Parameter(typeof(Attribute));
Expression idEqualExpression = Expression.Equal(
Expression.Property(attribute, nameof(Attribute.AttributeId)),
Expression.Constant(searchId)
);
Expression valueEqualExpression = Expression.Equal(
Expression.Property(attribute, nameof(Attribute.Value)),
Expression.Constant(searchValue)
);
Expression attributeMatchExpression = Expression.AndAlso(idEqualExpression, valueEqualExpression);
var matchLambda = Expression.Lambda(attributeMatchExpression, attribute);
return Expression.Call(typeof(Enumerable), nameof(Enumerable.Any), new [] {typeof(Attribute)}, attributes, matchLambda);
}
我有一个搜索设置,可以根据条件 return 匹配 Record
对象。它根据条件类型构建表达式树,并查询数据库以匹配 return。以前,它只匹配顶级属性,但现在我试图在 Record
的子属性中搜索并构建一个可以在查询中与其他属性组合的表达式。对象看起来像这样:
public class Record
{
public virtual ICollection<Attribute> Attributes { get; set; }
}
public class Attribute
{
public Guid AttributeId { get; set; }
public string Value { get; set; }
public virtual Record Record { get; set; }
}
可能的条件之一是 AttributeId
和 Value
的值对,为此我会 return 所有 Record
具有 [=18= 的行] 匹配两个值。例如,如果我只是编写一个简单的 LINQ 查询,它将如下所示:
Guid searchId = Guid.NewGuid();
string searchValue = "asdf";
IQueryable<Record> records = GetSomeQueryToRecords();
IEnumerable<Record> recordsThatMatch = records
.Where(r => r.Attribute.Any(a => a.AttributeId == searchId && a.Value == searchValue));
我需要将其构建到我的表达式树中,以便它在内存中和 EF Core 查询中都能工作。由 GetExpression
编辑的表达式 return 最终将与其他使用 AndAlso
的表达式组合,以构建一个大型查询。我试过这个:
ParameterExpression record = Expression.Parameter(typeof(Record));
Expression searchExpression = GetExpression(record, searchId, searchValue);
Expression<Func<Record, bool>> lambda = Expression.Lamda<Func<Record, bool>>(searchExpression, record);
recordsThatMatch = records.Where(lambda);
private Expression GetExpression(ParameterExpression parameter, Guid searchId, string searchValue)
{
// Get the Record.Attributes Memboer.
MemberExpression attributes = Expression.Property(parameter, nameof(Record.Attributes));
// Get the "record.Attributes.Any(predicate)" Method.
MethodInfo anyMethod = typeof(Enumerable).GetMethods()
.Where(m => m.Name == nameof(Enumerable.Any))
.Where(m => m.GetParameters().Length == 2)
.Single();
// Make Method generic.
anyMethod = anyMethod.MakeGenericMethod(typeof(Attribute));
ParameterExpression attribute = Expression.Parameter(typeof(Attribute));
Expression idEqualExpression = Expression.Equal(
Expression.Property(attribute, nameof(Attribute.AttributeId)),
Expression.Constant(searchId)
);
Expression valueEqualExpression = Expression.Equal(
Expression.Property(attribute, nameof(Attribute.Value)),
Expression.Constant(searchValue)
);
Expression attributeMatchExpression = Expression.AndAlso(idEqualExpression, valueEqualExpression);
return Expression.Call(null, anyMethod, attributes, attributeMatchExpression);
}
不过我得到了 ArgumentException
和 Expression.Call
,因为老实说我不知道如何将它用于这个特定实例,而且它有很多覆盖。我相信我应该传递 null
因为我实际上想要一个静态方法,因为 .Any()
是一个扩展方法。
您忘记创建 'LambdaExpresson'。还有Expression.Call
可以简化。
private Expression GetExpression(ParameterExpression parameter, Guid searchId, string searchValue)
{
// Get the Record.Attributes Memboer.
MemberExpression attributes = Expression.Property(parameter, nameof(Record.Attributes));
ParameterExpression attribute = Expression.Parameter(typeof(Attribute));
Expression idEqualExpression = Expression.Equal(
Expression.Property(attribute, nameof(Attribute.AttributeId)),
Expression.Constant(searchId)
);
Expression valueEqualExpression = Expression.Equal(
Expression.Property(attribute, nameof(Attribute.Value)),
Expression.Constant(searchValue)
);
Expression attributeMatchExpression = Expression.AndAlso(idEqualExpression, valueEqualExpression);
var matchLambda = Expression.Lambda(attributeMatchExpression, attribute);
return Expression.Call(typeof(Enumerable), nameof(Enumerable.Any), new [] {typeof(Attribute)}, attributes, matchLambda);
}