Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Issue around converting Expression<Func<T, bool>> to Expression<Func<U, bool>>

Issue around converting Expression<Func<T, bool>> to Expression<Func<U, bool>>

Scheduled Pinned Locked Moved C#
helpcsharpdatabaseregexquestion
5 Posts 3 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R Offline
    R Offline
    RichardGrimmer
    wrote on last edited by
    #1

    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

    Richard DeemingR S 2 Replies Last reply
    0
    • R RichardGrimmer

      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

      Richard DeemingR Offline
      Richard DeemingR Offline
      Richard Deeming
      wrote on last edited by
      #2

      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
      

      "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

      S 1 Reply Last reply
      0
      • Richard DeemingR Richard Deeming

        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
        
        S Offline
        S Offline
        Sascha Lefevre
        wrote on last edited by
        #3

        :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

        Richard DeemingR 1 Reply Last reply
        0
        • S Sascha Lefevre

          :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

          Richard DeemingR Offline
          Richard DeemingR Offline
          Richard Deeming
          wrote on last edited by
          #4

          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

          "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

          1 Reply Last reply
          0
          • R RichardGrimmer

            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

            S Offline
            S Offline
            Sascha Lefevre
            wrote on last edited by
            #5

            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

            1 Reply Last reply
            0
            Reply
            • Reply as topic
            Log in to reply
            • Oldest to Newest
            • Newest to Oldest
            • Most Votes


            • Login

            • Don't have an account? Register

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • World
            • Users
            • Groups