如何在 LINQ 表达式生成器中使用值数组?
How do I use an array of values in a LINQ Expression builder?
我想动态构建 LINQ 查询,这样我就可以做类似的事情
var list = n.Elements().Where(getQuery("a", "b"));
而不是
var list = n.Elements().Where(e => e.Name = new "a" || e.Name == "c");
(大多数时候,我需要传递带命名空间的 XName,而不仅仅是本地名称...)
我的问题是访问数组元素:
private static Func<XElement, bool> getQuery(XName[] names)
{
var param = Expression.Parameter(typeof(XElement), "e");
Expression exp = Expression.Constant(false);
for (int i = 0; i < names.Length; i++)
{
Expression eq = Expression.Equal(
Expression.Property(param, typeof(XElement).GetProperty("Name")!.Name),
/*--->*/ Expression.Variable(names[i].GetType(), "names[i]")
);
}
var lambda = Expression.Lambda<Func<XElement, bool>>(exp, param);
return lambda.Compile();
}
显然 Variable 表达式是错误的,但我很难构建能够访问数组值的表达式。
您需要创建一个表达式并编译它吗?除非我遗漏了一些细微差别,否则您所需要的只是一个 returns a Func<XElement, bool>
.
的函数
private Func<XElement, bool> GetQuery(params string[] names)
{
return element => names.Any(n => element.Name == n);
}
这需要一个字符串数组和 returns 一个 Func<XElement>
。如果元素名称与任何参数匹配,则该函数 returns 为真。
然后您可以按照您的描述使用它:
var list = n.Elements.Where(GetQuery("a", "b"));
有很多方法可以做到这一点。为了提高可读性,这样的扩展可能更好:
public static class XElementExtensions
{
public static IEnumerable<XElement> WhereNamesMatch(
this IEnumerable<XElement> elements,
params string[] names)
{
return elements.Where(element =>
names.Any(n => element.Name == n));
}
}
那么使用它的代码就变成了
var list = n.Elements.WhereNamesMatch("a", "b");
当我们的 LINQ 查询中有其他筛选器时,这尤其有用。所有 Where
和其他方法都会变得难以阅读。但是,如果我们将它们隔离到具有清晰名称的自己的函数中,那么用法更容易阅读,并且我们可以在不同的查询中重新使用扩展。
如果你想把它写成表达式,你可以这样做:
public static Expression<Func<Person, bool>> GetQuery(Person[] names)
{
var parameter = Expression.Parameter(typeof(Person), "e");
var propertyInfo = typeof(Person).GetProperty("Name");
var expression = names.Aggregate(
(Expression)Expression.Constant(false),
(acc, next) => Expression.MakeBinary(
ExpressionType.Or,
acc,
Expression.Equal(
Expression.Constant(propertyInfo.GetValue(next)),
Expression.Property(parameter, propertyInfo))));
return Expression.Lambda<Func<Person, bool>>(expression, parameter);
}
是否编译表达式取决于您要实现的方式。如果您想将表达式传递给查询提供程序(参见 Queryable.Where)并具有例如数据库过滤你的值,那么你可能无法编译表达式。
如果要过滤内存中的集合,即枚举所有元素(参见Enumerable.Where)并将谓词应用于所有元素,则必须编译表达式。在这种情况下,您可能不应该使用表达式 api,因为这会增加代码的复杂性,并且您更容易受到运行时错误的影响。
我想动态构建 LINQ 查询,这样我就可以做类似的事情
var list = n.Elements().Where(getQuery("a", "b"));
而不是
var list = n.Elements().Where(e => e.Name = new "a" || e.Name == "c");
(大多数时候,我需要传递带命名空间的 XName,而不仅仅是本地名称...)
我的问题是访问数组元素:
private static Func<XElement, bool> getQuery(XName[] names)
{
var param = Expression.Parameter(typeof(XElement), "e");
Expression exp = Expression.Constant(false);
for (int i = 0; i < names.Length; i++)
{
Expression eq = Expression.Equal(
Expression.Property(param, typeof(XElement).GetProperty("Name")!.Name),
/*--->*/ Expression.Variable(names[i].GetType(), "names[i]")
);
}
var lambda = Expression.Lambda<Func<XElement, bool>>(exp, param);
return lambda.Compile();
}
显然 Variable 表达式是错误的,但我很难构建能够访问数组值的表达式。
您需要创建一个表达式并编译它吗?除非我遗漏了一些细微差别,否则您所需要的只是一个 returns a Func<XElement, bool>
.
private Func<XElement, bool> GetQuery(params string[] names)
{
return element => names.Any(n => element.Name == n);
}
这需要一个字符串数组和 returns 一个 Func<XElement>
。如果元素名称与任何参数匹配,则该函数 returns 为真。
然后您可以按照您的描述使用它:
var list = n.Elements.Where(GetQuery("a", "b"));
有很多方法可以做到这一点。为了提高可读性,这样的扩展可能更好:
public static class XElementExtensions
{
public static IEnumerable<XElement> WhereNamesMatch(
this IEnumerable<XElement> elements,
params string[] names)
{
return elements.Where(element =>
names.Any(n => element.Name == n));
}
}
那么使用它的代码就变成了
var list = n.Elements.WhereNamesMatch("a", "b");
当我们的 LINQ 查询中有其他筛选器时,这尤其有用。所有 Where
和其他方法都会变得难以阅读。但是,如果我们将它们隔离到具有清晰名称的自己的函数中,那么用法更容易阅读,并且我们可以在不同的查询中重新使用扩展。
如果你想把它写成表达式,你可以这样做:
public static Expression<Func<Person, bool>> GetQuery(Person[] names)
{
var parameter = Expression.Parameter(typeof(Person), "e");
var propertyInfo = typeof(Person).GetProperty("Name");
var expression = names.Aggregate(
(Expression)Expression.Constant(false),
(acc, next) => Expression.MakeBinary(
ExpressionType.Or,
acc,
Expression.Equal(
Expression.Constant(propertyInfo.GetValue(next)),
Expression.Property(parameter, propertyInfo))));
return Expression.Lambda<Func<Person, bool>>(expression, parameter);
}
是否编译表达式取决于您要实现的方式。如果您想将表达式传递给查询提供程序(参见 Queryable.Where)并具有例如数据库过滤你的值,那么你可能无法编译表达式。
如果要过滤内存中的集合,即枚举所有元素(参见Enumerable.Where)并将谓词应用于所有元素,则必须编译表达式。在这种情况下,您可能不应该使用表达式 api,因为这会增加代码的复杂性,并且您更容易受到运行时错误的影响。