.net core - 将函数列表与单个函数组合或合并到单个函数
.net core - combine a list of func with or to a single func
你好,我尝试从由 or 组合的列表中生成单个 Func。
var funcs = new List<Func<User, bool>>()
{
(u) => u.Id.Equals(entityToFind.Id),
(u) => u.UserName == entityToFind.UserName,
(u) => u.Email == entityToFind.Email
};
//TODO: Some magic that funs is euqaly to that:
Func<User, bool> func = (u) => u.Id.Equals(entityToFind.Id) || u.UserName == entityToFind.UserName || u.Email == entityToFind.Email;
我也用表达式试过,像这样:
private Dictionary<string, Expression<Func<User, bool>>> private Dictionary<string, Expression<Func<User, bool>>> test(User entityToFind)
{
return new Dictionary<string, Expression<Func<User, bool>>>() {
{"Id", (u) => u.Id.Equals(entityToFind.Id) },
{"Name", (u) => u.UserName == entityToFind.UserName },
{"Email", (u) => u.Email == entityToFind.Email }
};
}
public static Expression<Func<T, bool>> ToOrExpression<T>(this Dictionary<string, Expression<Func<T, bool>>> dict)
{
var expressions = dict.Values.ToList();
if (!expressions.Any())
{
return t => true;
}
var delegateType = typeof(Func<T, bool>)
.GetGenericTypeDefinition()
.MakeGenericType(new[]
{
typeof(T),
typeof(bool)
}
);
var tfd = Expression.OrElse(expressions[0], expressions[1]);
var combined = expressions
.Cast<Expression>()
.Aggregate( (e1, e2) => Expression.OrElse(e1, e2) );
return (Expression<Func<T, bool>>)Expression.Lambda(delegateType, combined);
}
test(entityToFind).ToOrExpression();
但是我会得到以下错误:
没有为类型'System.Func2[Models.User,System.Boolean]' and
'System.Func
2[Models.User,System.Boolean]'定义二元运算符 OrElse
虽然您可以创建一个包装器方法来组合一堆 Func
,因为您使用的是 Entity Framework,这会导致整个数据集被下载到内存中并在本地完成搜索.您应该使用 Expression<Func<T, bool>>
代替。
幸运的是Marc Gravell has already written a handy bit of code to combine expressions。您的问题严格来说是重复的,因为您想将两个以上的问题组合在一起,但是使用一点 Linq 就很容易。所以,让我们先从你的表达式开始,代码几乎没有变化:
var expressions = new List<Expression<Func<User, bool>>>()
{
(u) => u.Id.Equals(entityToFind.Id),
(u) => u.UserName == entityToFind.UserName,
(u) => u.Email == entityToFind.Email
};
现在使用 Marc 的代码并将其修改为 or
而不是 and
:
public static class ExpressionExtensions
{
public static Expression<Func<T, bool>> OrElse<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof(T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
Expression.OrElse(left, right), parameter);
}
private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
}
不,您将表达式与 Linq Aggregate
方法组合:
var combinedExpression = expressions.Aggregate((x, y) => x.OrElse(y));
并像这样使用它:
var result = db.Things.Where(combinedExpression);
你好,我尝试从由 or 组合的列表中生成单个 Func。
var funcs = new List<Func<User, bool>>()
{
(u) => u.Id.Equals(entityToFind.Id),
(u) => u.UserName == entityToFind.UserName,
(u) => u.Email == entityToFind.Email
};
//TODO: Some magic that funs is euqaly to that:
Func<User, bool> func = (u) => u.Id.Equals(entityToFind.Id) || u.UserName == entityToFind.UserName || u.Email == entityToFind.Email;
我也用表达式试过,像这样:
private Dictionary<string, Expression<Func<User, bool>>> private Dictionary<string, Expression<Func<User, bool>>> test(User entityToFind)
{
return new Dictionary<string, Expression<Func<User, bool>>>() {
{"Id", (u) => u.Id.Equals(entityToFind.Id) },
{"Name", (u) => u.UserName == entityToFind.UserName },
{"Email", (u) => u.Email == entityToFind.Email }
};
}
public static Expression<Func<T, bool>> ToOrExpression<T>(this Dictionary<string, Expression<Func<T, bool>>> dict)
{
var expressions = dict.Values.ToList();
if (!expressions.Any())
{
return t => true;
}
var delegateType = typeof(Func<T, bool>)
.GetGenericTypeDefinition()
.MakeGenericType(new[]
{
typeof(T),
typeof(bool)
}
);
var tfd = Expression.OrElse(expressions[0], expressions[1]);
var combined = expressions
.Cast<Expression>()
.Aggregate( (e1, e2) => Expression.OrElse(e1, e2) );
return (Expression<Func<T, bool>>)Expression.Lambda(delegateType, combined);
}
test(entityToFind).ToOrExpression();
但是我会得到以下错误:
没有为类型'System.Func2[Models.User,System.Boolean]' and
'System.Func
2[Models.User,System.Boolean]'定义二元运算符 OrElse
虽然您可以创建一个包装器方法来组合一堆 Func
,因为您使用的是 Entity Framework,这会导致整个数据集被下载到内存中并在本地完成搜索.您应该使用 Expression<Func<T, bool>>
代替。
幸运的是Marc Gravell has already written a handy bit of code to combine expressions。您的问题严格来说是重复的,因为您想将两个以上的问题组合在一起,但是使用一点 Linq 就很容易。所以,让我们先从你的表达式开始,代码几乎没有变化:
var expressions = new List<Expression<Func<User, bool>>>()
{
(u) => u.Id.Equals(entityToFind.Id),
(u) => u.UserName == entityToFind.UserName,
(u) => u.Email == entityToFind.Email
};
现在使用 Marc 的代码并将其修改为 or
而不是 and
:
public static class ExpressionExtensions
{
public static Expression<Func<T, bool>> OrElse<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof(T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
Expression.OrElse(left, right), parameter);
}
private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
}
不,您将表达式与 Linq Aggregate
方法组合:
var combinedExpression = expressions.Aggregate((x, y) => x.OrElse(y));
并像这样使用它:
var result = db.Things.Where(combinedExpression);