如何组合这些表达
Expression<Func<int, int>> f1 = i => i + 1;
Expression<Func<int, int>> f2 = i => i + 2;
Expression<Func<int, int, int>> f3 = (i, j) => i * j;
到
Expression<Func<int, int>> f4 = i =>(i+1)*(i+2);
跑步时?
这是代码。我想编写一个extend方法,但它在linq2entities中不起作用
public static IQueryable<TRe> LeftJoin<TLeft, TRight, TKey, TRe>(this IQueryable<TLeft> left, IQueryable<TRight> right, Expression<Func<TLeft, TKey>> leftKeySel, Expression<Func<TRight, TKey>> rightKeySel, Expression<Func<TLeft, TRight, TRe>> reSel)
{
return left.GroupJoin(right, leftKeySel, rightKeySel, (l, r) => new { l, r }).SelectMany(p => p.r.DefaultIfEmpty(), (p, r) => new { p.l, r }).Select1(p => p.l, p => p.r, reSel);
}
public static IQueryable<TRe> Select1<TSrc, T1, T2, TRe>(this IQueryable<TSrc> src, Expression<Func<TSrc, T1>> f1, Expression<Func<TSrc, T2>> f2, Expression<Func<T1, T2, TRe>> func)
{
var p = Expression.Parameter(typeof(TSrc));
var a = Expression.Invoke(f1, p);
var b = Expression.Invoke(f2, p);
var c = Expression.Invoke(func, a, b);
return src.Select(Expression.Lambda<Func<TSrc, TRe>>(c, p));
}
这个代码叫做LeftJoin方法:
var re = _db.Accounts.OrderBy(p => p.LoginDays).Take(100).LeftJoin(_db.PasswordHistorys, p => p.Email, p => p.Email, (a, b) => new
{
a,
b.PasswordOld
});
答案 0 :(得分:3)
你可以这样做:
var p = Expression.Parameter(typeof(int), "i");
var r = Expression
.Invoke(f3, new[] {
Expression.Invoke(f1, p),
Expression.Invoke(f2, p) });
Expression<Func<int, int>> lam = Expression.Lambda<Func<int, int>>(r, p);
根据评论,可以这样做:
var p = Expression.Parameter(typeof(int), "i");
var lam = Expression.Lambda<Func<int, int>>(Expression.Multiply(f1.Body, f2.Body), p);
或
var p = Expression.Parameter(typeof(int), "i");
var lam = Expression.Lambda<Func<int, int>>(Expression.Add(f1.Body, f2.Body), p);
所以,我设法用f1
和f2
来替换参数引用。
但是,它做出以下假设:
f3
只有两个参数。 f1
,f2
和f3
都具有完全相同的方法签名以下是实施:
public class SuperHack : ExpressionVisitor
{
private Dictionary<ParameterExpression, LambdaExpression> _replacements;
private ParameterExpression _newParameter;
public SuperHack(Dictionary<ParameterExpression, LambdaExpression> replacements, ParameterExpression newParameter)
{
_replacements = replacements ?? new Dictionary<ParameterExpression, LambdaExpression>();
_newParameter = newParameter;
}
public Expression Modify(Expression expression)
{
var res = Visit(expression);
return res;
}
protected override Expression VisitLambda<T>(Expression<T> e)
{
return Expression.Lambda(Visit(e.Body), _newParameter);
}
protected override Expression VisitParameter(ParameterExpression e)
{
if (_replacements.ContainsKey(e))
return Visit(Expression.Lambda(_replacements[e].Body, _newParameter).Body);
return base.VisitParameter(_newParameter);
}
}
以下是你如何使用它:
Expression<Func<int, int>> f1 = i => i + 1;
Expression<Func<int, int>> f2 = i => i + 2;
Expression<Func<int, int, int>> f3 = (i, j) => i * j;
var @params = f3.Parameters;
var mapping = new Dictionary<ParameterExpression, LambdaExpression>
{
{@params[0], f1},
{@params[1], f2}
};
var p = Expression.Parameter(typeof(int), "i");
var f4 = new SuperHack(mapping, p).Modify(f3) as Expression<Func<int,int>>;
结果是:
i => ((i + 1) * (i + 2))
不需要调用!
答案 1 :(得分:0)
我对我的其他解决方案并不完全满意(虽然它可能对那些不关心表达式中Invoke
次呼叫的人有所帮助,所以我会把它留下来)。
用实际表达式替换参数的解决方法充其量只是非常脆弱,而且非常脆弱。经过一番思考后,简单地用所有Invoke
调用替换它们各自的表达式,用参数替换参数似乎更合乎逻辑。
这使您可以编写更复杂的查询,并且可能对从Linq-To-Sql
迁移到EntityFramework
的人有用。它还使我们能够进行进一步的调整,以使EntityFramework
能够正常运行。
最后写下这样的东西:
var p = Expression.Parameter(typeof(int), "i");
var r = Expression
.Invoke(f3, new[] {
Expression.Invoke(f1, p),
Expression.Invoke(f2, p) })
.InlineInvokes();
Expression<Func<int, int>> lam = Expression.Lambda<Func<int, int>>(r, p);
采用这种表达方式:
i => Invoke((i, j) => (i * j), Invoke(i => (i + 1), i), Invoke(i => (i + 2), i))
用这个替换它:
i => ((i + 1) * (i + 2))
或者更复杂:
b => Invoke((d, e) => (d * e), Invoke(b => (50 + Invoke(z => (25 + Invoke(h => (h * 8), z)), b)), b), Invoke(c => (c + 2), b))
可生产
b => ((50 + (25 + (b * 8))) * (b + 2))
实现:
public static class ExpressionHelpers
{
public static Expression InlineInvokes<T>(this T expression)
where T : Expression
{
return (T)new InvokeInliner().Inline(expression);
}
public static Expression InlineInvokes(this InvocationExpression expression)
{
return new InvokeInliner().Inline(expression);
}
public class InvokeInliner : ExpressionVisitor
{
private Stack<Dictionary<ParameterExpression, Expression>> _context = new Stack<Dictionary<ParameterExpression, Expression>>();
public Expression Inline(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitInvocation(InvocationExpression e)
{
var callingLambda = ((LambdaExpression)e.Expression);
var currentMapping = new Dictionary<ParameterExpression, Expression>();
for (var i = 0; i < e.Arguments.Count; i++)
{
var argument = Visit(e.Arguments[i]);
var parameter = callingLambda.Parameters[i];
if (parameter != argument)
currentMapping.Add(parameter, argument);
}
_context.Push(currentMapping);
var result = Visit(callingLambda.Body);
_context.Pop();
return result;
}
protected override Expression VisitParameter(ParameterExpression e)
{
if (_context.Count > 0)
{
var currentMapping = _context.Peek();
if (currentMapping.ContainsKey(e))
return currentMapping[e];
}
return e;
}
}
}
工作原理:
每当我们点击Invoke
表达式时,我们都会存储传递给调用的值,以及要调用的表达式的参数。例如,如果我们有这样的调用:
Invoke(f1, Expression.Constant(1))
和f1
定义为i => { i + 1; }
我们定义了i => Expression.Constant(1)
然后我们继续解析lambda表达式 body ,因为我们不再使用参数。
然后我们捕获对Parameter
的访问。在这里,我们看一下定义的当前映射。如果有参数的映射,我们将逐字地返回替换值。如果没有映射,我们只需返回参数。