ParameterExpression param = expr1.Parameters[0]; if (ReferenceEquals(param, expr2.Parameters[0])) { return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, expr2.Body), param); } return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, Expression.Invoke(expr2, param)), param);
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To post to this group, send email to nhu...@googlegroups.com.
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.
ParameterExpression param = expr1.Parameters[0];
if (ReferenceEquals(param, expr2.Parameters[0]))
{
// simple versionreturn Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, expr2.Body), param); }
// otherwise, keep expr1 "as is" and invoke expr2return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, Expression.Invoke(expr2, param)), param);
The change I proposed (in re-linq) would make Invoke just work in all
re-linq-based providers.
The workaround using the ReplacingExpressionTreeVisitor completely
avoids InvocationExpressions, and I strongly believe it will work
right now, with the existing NHibernate 3.0.
Where exactly am I wrong?
> ParameterExpression param = expr1.Parameters[0];
> if (ReferenceEquals(param, expr2.Parameters[0]))
> {
> return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body,
> expr2.Body), param);
> }
> return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body,
> Expression.Invoke(expr2, param)), param);
I'm not sure if the workaround you've posted will always work: you're
still using Expression.Invoke in those cases where the parameters are
not the same for both expressions. I believe that those cases would
still cause the exception described by the original poster.
Fabian
> There is a modified version of the PredicateBuilder that works fine
> with NH3. It has a little override to the ExpressionBuilder that
> renames the lambda parameter and rebuilds the expression tree.
>
> It can be found there:
> http://www.primordialcode.com/Blog/Post/nhibernate-linq-dynamic-filtering-lambda-expressions
Great! Just a few comments:
- Since NHibernate uses re-linq, it already contains an Expression
Visitor base class (ExpressionTreeVisitor) - no need to implement your
own.
- As I've posted before, re-linq also contains a
ReplacingExpressionTreeVisitor class that can be used to replace the
parameters. No need to define your own ParameterModifier.
- In your RebuildExpressionIfNeeded method, you check if "p.Name !=
expr2.Parameters[0].Name". Maybe NHibernate has special code to also
compare parameter names, but usually, parameter expressions are
compared by reference, not by name, so you'd need to change the check
to: "p != expr2.Parameters[0]". You can omit rebuilding the expression
tree only if the references match. In fact, I just tried with Linq to
Objects, and your code doesn't work there unless you change the check
in RebuildExpressionIfNeeded.
- Your ParameterModifier will yield invalid results if there is any
other ParameterExpression involved in the expression tree besides the
one you want to replace (eg, in the nested LambdaExpression case you
hint about below). To overcome this, change it so that it compares the
current expression in VisitParameter with the original parameter
expression (by reference), and perform the replacement only if they
match.
For completeness, here's again my own variation of And and Or using
re-linq's ReplacingExpressionTreeVisitor:
public static Expression<Func<T, bool>> And<T> (
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var adaptedExpr2Body = ReplacingExpressionTreeVisitor.Replace (
expr2.Parameters[0],
expr1.Parameters[0],
expr2.Body);
return Expression.Lambda<Func<T, bool>> (
Expression.AndAlso (expr1.Body, adaptedExpr2Body),
expr1.Parameters);
}
public static Expression<Func<T, bool>> Or<T> (
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var adaptedExpr2Body = ReplacingExpressionTreeVisitor.Replace (
expr2.Parameters[0],
expr1.Parameters[0],
expr2.Body);
return Expression.Lambda<Func<T, bool>> (
Expression.AndAlso (expr1.Body, adaptedExpr2Body),
expr1.Parameters);
}
> I did encouter a problem with that solution when nested lambdas are in
> place and there is no need to rename the parameter of the nested
> lambda.
> I changed the code slightly to overcome this scenario:
>
> protected override Expression VisitParameter(ParameterExpression p)
> {
> if (p.Type == _newParam.Type)
> return _newParam;
> else
> return p;
> }
You should really compare to the old parameter by reference instead.
Otherwise you'll always have edge cases not handled correctly.
Regards,
Fabian
Sorry, my mistake. I've also posted a comment to that blog, so the
author should be aware of the issues by now.
Fabian