Issue around converting Expression<Func<T, bool>> to Expression<Func<U, bool>>
-
I'm in the process of breaking the link we currently have between EF and our application code. In order to do this, I've created a collection of "Domain" objects to match our "Database" objects, so I have an Entity Framework Account type, and a Domain Account type, both of which have the same fields, notably an int AccountId field. I've also implemented a simple repository to allow us to pull domain objects. The issue I'm having is I my Find method - the prototype of which is;
Find(Expression> predicate)
Obviously, this method deals with domain objects. However, in the EF specific implementation of the repo I'm working on, I can't simply apply the same predicate to a Where() since the predicate refers to domain types rather than database types. To that end, I've been looking at the ExpressionVisitor class in order to "mutate" the expression into one that deals with database types. Since I want to change the Parameter, I've created the following implementation;
public class ExpressionParameterModificationVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}protected override Expression VisitParameter(ParameterExpression expression) { return Expression.Parameter(typeof (TTo)); }
}
This appears to work as intended (based on examining what I get and my *limited* understanding of this), however, when I attempt to use it;
Expression> predicate = account => account.AccountId == 1;
I'm seeing the error;
Additional information: Property 'Int32 AccountId' is not defined for type 'Data.Account'
The Data.Account type definitely has the correct field, so I'm assuming I need to do more with my Visitor, and override another method - the obvious suspect is to do the following;
protected override Expression VisitMember(MemberExpression node)
{
return Expression.MakeMemberAccess (base.Visit(node.Expression), typeof(TTo).GetProperty(node.Member.Name));
}Now the issue I'm seeing is that the VisitMember method is called before the VisitParameter method, and hence I'm getting;
ParameterExpression of type 'Data.Account' cannot be used for delegate parameter of type 'Domain.Account'
Perhaps I'm approaching the issue incorrectly, but anyone have any ideas what I'm missing here?
C# has already designed away
-
I'm in the process of breaking the link we currently have between EF and our application code. In order to do this, I've created a collection of "Domain" objects to match our "Database" objects, so I have an Entity Framework Account type, and a Domain Account type, both of which have the same fields, notably an int AccountId field. I've also implemented a simple repository to allow us to pull domain objects. The issue I'm having is I my Find method - the prototype of which is;
Find(Expression> predicate)
Obviously, this method deals with domain objects. However, in the EF specific implementation of the repo I'm working on, I can't simply apply the same predicate to a Where() since the predicate refers to domain types rather than database types. To that end, I've been looking at the ExpressionVisitor class in order to "mutate" the expression into one that deals with database types. Since I want to change the Parameter, I've created the following implementation;
public class ExpressionParameterModificationVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}protected override Expression VisitParameter(ParameterExpression expression) { return Expression.Parameter(typeof (TTo)); }
}
This appears to work as intended (based on examining what I get and my *limited* understanding of this), however, when I attempt to use it;
Expression> predicate = account => account.AccountId == 1;
I'm seeing the error;
Additional information: Property 'Int32 AccountId' is not defined for type 'Data.Account'
The Data.Account type definitely has the correct field, so I'm assuming I need to do more with my Visitor, and override another method - the obvious suspect is to do the following;
protected override Expression VisitMember(MemberExpression node)
{
return Expression.MakeMemberAccess (base.Visit(node.Expression), typeof(TTo).GetProperty(node.Member.Name));
}Now the issue I'm seeing is that the VisitMember method is called before the VisitParameter method, and hence I'm getting;
ParameterExpression of type 'Data.Account' cannot be used for delegate parameter of type 'Domain.Account'
Perhaps I'm approaching the issue incorrectly, but anyone have any ideas what I'm missing here?
C# has already designed away
This sort of expression rewriting can get very complicated. Something like this should work for fairly simple expressions:
public sealed class ExpressionParameterModificationVisitor<TFrom, TTo> : ExpressionVisitor
{
private readonly Stack<Dictionary<ParameterExpression, ParameterExpression>> _parameterMaps = new Stack<Dictionary<ParameterExpression, ParameterExpression>>();protected override Expression VisitLambda<T>(Expression<T> node) { var parameters = VisitAndConvert<ParameterExpression>(node.Parameters, "VisitLambda"); var parameterMap = node.Parameters.Zip(parameters, (source, dest) => new { source, dest }).ToDictionary(p => p.source, p => p.dest); \_parameterMaps.Push(parameterMap); try { var body = Visit(node.Body); return Expression.Lambda(body, parameters); } finally { \_parameterMaps.Pop(); } } protected override Expression VisitParameter(ParameterExpression expression) { if (typeof(TFrom) != expression.Type) return expression; return Expression.Parameter(typeof(TTo), expression.Name); } private Expression MapParameter(Expression expression) { var param = expression as ParameterExpression; if (param != null) { ParameterExpression result; if (\_parameterMaps.Count != 0 && \_parameterMaps.Peek().TryGetValue(param, out result)) { expression = result; } } return expression; } protected override Expression VisitMember(MemberExpression node) { if (typeof(TFrom) != node.Expression.Type) return node.Update(Visit(node.Expression)); var expression = MapParameter(node.Expression); var member = typeof(TTo).GetProperty(node.Member.Name); return Expression.MakeMemberAccess(expression, member); } public static Expression<Func<TTo, TResult>> Modify<TResult>(Expression<Func<TFrom, TResult>> expression) { if (expression == null) throw new ArgumentNullException(nameof(expression)); var visitor = new ExpressionParameterModificationVisitor<TFrom, TTo>(); var result = visitor.Visit(expression); return (Expression<Func<TTo, TResult>>)result
-
This sort of expression rewriting can get very complicated. Something like this should work for fairly simple expressions:
public sealed class ExpressionParameterModificationVisitor<TFrom, TTo> : ExpressionVisitor
{
private readonly Stack<Dictionary<ParameterExpression, ParameterExpression>> _parameterMaps = new Stack<Dictionary<ParameterExpression, ParameterExpression>>();protected override Expression VisitLambda<T>(Expression<T> node) { var parameters = VisitAndConvert<ParameterExpression>(node.Parameters, "VisitLambda"); var parameterMap = node.Parameters.Zip(parameters, (source, dest) => new { source, dest }).ToDictionary(p => p.source, p => p.dest); \_parameterMaps.Push(parameterMap); try { var body = Visit(node.Body); return Expression.Lambda(body, parameters); } finally { \_parameterMaps.Pop(); } } protected override Expression VisitParameter(ParameterExpression expression) { if (typeof(TFrom) != expression.Type) return expression; return Expression.Parameter(typeof(TTo), expression.Name); } private Expression MapParameter(Expression expression) { var param = expression as ParameterExpression; if (param != null) { ParameterExpression result; if (\_parameterMaps.Count != 0 && \_parameterMaps.Peek().TryGetValue(param, out result)) { expression = result; } } return expression; } protected override Expression VisitMember(MemberExpression node) { if (typeof(TFrom) != node.Expression.Type) return node.Update(Visit(node.Expression)); var expression = MapParameter(node.Expression); var member = typeof(TTo).GetProperty(node.Member.Name); return Expression.MakeMemberAccess(expression, member); } public static Expression<Func<TTo, TResult>> Modify<TResult>(Expression<Func<TFrom, TResult>> expression) { if (expression == null) throw new ArgumentNullException(nameof(expression)); var visitor = new ExpressionParameterModificationVisitor<TFrom, TTo>(); var result = visitor.Visit(expression); return (Expression<Func<TTo, TResult>>)result
:thumbsup: Just a small kind of typo:
public static Expression> Modify(Expression> expression)
^^^^Don't tell me you just typed that in Notepad!?
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
-
:thumbsup: Just a small kind of typo:
public static Expression> Modify(Expression> expression)
^^^^Don't tell me you just typed that in Notepad!?
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
LINQPad, but I changed the
Modify
method as I posted it. Fixed. :)
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
I'm in the process of breaking the link we currently have between EF and our application code. In order to do this, I've created a collection of "Domain" objects to match our "Database" objects, so I have an Entity Framework Account type, and a Domain Account type, both of which have the same fields, notably an int AccountId field. I've also implemented a simple repository to allow us to pull domain objects. The issue I'm having is I my Find method - the prototype of which is;
Find(Expression> predicate)
Obviously, this method deals with domain objects. However, in the EF specific implementation of the repo I'm working on, I can't simply apply the same predicate to a Where() since the predicate refers to domain types rather than database types. To that end, I've been looking at the ExpressionVisitor class in order to "mutate" the expression into one that deals with database types. Since I want to change the Parameter, I've created the following implementation;
public class ExpressionParameterModificationVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}protected override Expression VisitParameter(ParameterExpression expression) { return Expression.Parameter(typeof (TTo)); }
}
This appears to work as intended (based on examining what I get and my *limited* understanding of this), however, when I attempt to use it;
Expression> predicate = account => account.AccountId == 1;
I'm seeing the error;
Additional information: Property 'Int32 AccountId' is not defined for type 'Data.Account'
The Data.Account type definitely has the correct field, so I'm assuming I need to do more with my Visitor, and override another method - the obvious suspect is to do the following;
protected override Expression VisitMember(MemberExpression node)
{
return Expression.MakeMemberAccess (base.Visit(node.Expression), typeof(TTo).GetProperty(node.Member.Name));
}Now the issue I'm seeing is that the VisitMember method is called before the VisitParameter method, and hence I'm getting;
ParameterExpression of type 'Data.Account' cannot be used for delegate parameter of type 'Domain.Account'
Perhaps I'm approaching the issue incorrectly, but anyone have any ideas what I'm missing here?
C# has already designed away
As Richard's solution is working fine there's no practical need for this, but in case you're interested, for the purpose of learning about building Expressions: I answered a same, earlier question with a solution that does this without the use of
ExpressionVisitor
: Mapping expressions Expression<Func<UserVM, object>> to Expression<Func<User, object>>[^]If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson