Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am busy creating wrapper extension methods on top of Dapper and DapperExtensions . At the moment I am trying to add filtering to the GetList<T> extension method, similar to LINQ's Where<T> extension method. I have seen this question but it seems I cannot implement what Marc Gravell suggested because there isn't a type EqualsExpression in .NET 4.5. Here is some demo code to help with the explanation of my problem:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq.Expressions;
using DapperExtensions;
namespace Dapper.Extensions.Demo
    public class Program
        private static readonly string ConnectionString = ConfigurationManager.ConnectionStrings["DapperDbContext"].ConnectionString;
        public static IDbConnection Connection { get { return new SqlConnection(ConnectionString); } }
        public static void Main(string[] args)
            const int marketId = 2;
            var matchingPeople = Connection.Get<Person>(p => p.MarketId, marketId); // This works
            // Below is a LambdaExpression. expression.Body is, bizarrely, a UnaryExpression with a Convert
            //var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId); // Does not work
            foreach (var person in matchingPeople)
                Console.WriteLine(person);
            if (Debugger.IsAttached)
                Console.ReadLine();
    public static class SqlConnectionExtensions
        public static IEnumerable<T> Get<T>(this IDbConnection connection, Expression<Func<T, object>> expression, object value = null) where T : class
            using (connection)
                connection.Open();
                // I want to be able to pass in: t => t.Id == id then:
                // Expression<Func<T, object>> expressionOnLeftOfFilterClause = t => t.Id;
                // string operator = "==";
                // object valueFromLambda = id;
                // and call Predicates.Field(expressionOnLeftOfFilterClause, Operator.Eq, valueFromLambda)
                var predicate = Predicates.Field(expression, Operator.Eq, value);
                var entities = connection.GetList<T>(predicate, commandTimeout: 30);
                connection.Close();
                return entities;
    public class Person
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string Surname { get; set; }
        public int MarketId { get; set; }
        public override string ToString()
            return string.Format("{0}: {1}, {2} - MarketId: {3}", Id, Surname, FirstName, MarketId);

Paying particular attention to my Get<T> extension method: when I pass in either p => p.MarketId or p => p.MarketId == marketId, expression.Body is of type UnaryExpression. For the latter, expression.Body actually contains {Convert((p.MarketId == 2))}.

Attempting

var binaryExpression = expression as BinaryExpression;

returns null, which is unfortunate because there are Left and Right properties which I could have found useful.

So, does anyone know how to achieve what I want? Further down the line I would like to be able to pick the Operator enum based on the lambda expression passed in. Any help would be much appreciated.

My guess would be that the Operand property of the UnaryExpression will be the BinaryExpression you're looking for. – Iridium Mar 1, 2013 at 9:53 @Iridium I think you're right; through debugging I can see that the Operand of expression.Body is of type LogicalBinaryExpression but how do I access expression.Body.Operand in-code? Intellisense cannot resolve that? – Sameer Singh Mar 1, 2013 at 10:00 @Iridium is absolutely right, just tried it now: ` BinaryExpression binary = (expr.Body as UnaryExpression).Operand as BinaryExpression;` yields the much desired BinaryExpression. @Jon Skeet explained to you perfectly why the conversion appears (value types being boxed to object)... Beware of even more complex expression trees that you yourself might send yourself. You cannot do an expression tree parser that has a high spectrum of possibilities on the user programmer side and make very big assumptions on the library writer side... – Eduard Dumitru Mar 1, 2013 at 10:04 I'm sorry to say this but before starting to write libraries (not to mention LINQ parsers) I suggest you try to gain a better understanding of basic OOP. An expression tree is a subset of an "Abstract Syntax Tree". That means that each node is of an abstract type (syntactically) which at runtime might end up to be a more concrete type which in turn contains one ore more abstract instances (which at runtime end up to be something). This abstract / concrete tango dance is what makes your IDE's Intellisense not to work. You must assume things, test their validity and act accordingly by subcasting – Eduard Dumitru Mar 1, 2013 at 10:09 @Eduard I am perfectly aware of OOP principles. I know about abstract and concrete types and I am also aware that I could pass in a more complex lambda expression which I will need to parse. Admittedly I have not spent much time constructing expression trees in-code which is why I posted the question in the first place. And yes I have skimmed MSDN for pointers. – Sameer Singh Mar 1, 2013 at 10:19
  • I need an extension method which wraps DapperExtension's GetList<T> extension method.
  • The latter may take in a predicate of type IFieldPredicate which I can use to add a filter to the SQL query to be executed. I can achieve this by using Predicates.Field<T>(Expression<Func<T, object>> expression, Operator op, object value).
  • The problem lies in transforming a simple lambda expression t => t.Id == id into parameters for Predicates.Field<T>. So, conceptually, I need to pull apart the lambda expression into three parts: t => t.Id, Operator.Eq, and id.
  • With help from @Iridium, @Eduard and @Jon, my final solution is:

    public static class SqlConnectionExtensions
        public static IEnumerable<T> Get<T>(this IDbConnection connection, Expression<Func<T, object>> expression) where T : class
            using (connection)
                connection.Open();
                var binaryExpression = (BinaryExpression)((UnaryExpression) expression.Body).Operand;
                var left = Expression.Lambda<Func<T, object>>(Expression.Convert(binaryExpression.Left, typeof(object)), expression.Parameters[0]);
                var right = binaryExpression.Right.GetType().GetProperty("Value").GetValue(binaryExpression.Right);
                var theOperator = DetermineOperator(binaryExpression);
                var predicate = Predicates.Field(left, theOperator, right);
                var entities = connection.GetList<T>(predicate, commandTimeout: 30);
                connection.Close();
                return entities;
        private static Operator DetermineOperator(Expression binaryExpression)
            switch (binaryExpression.NodeType)
                case ExpressionType.Equal:
                    return Operator.Eq;
                case ExpressionType.GreaterThan:
                    return Operator.Gt;
                case ExpressionType.GreaterThanOrEqual:
                    return Operator.Ge;
                case ExpressionType.LessThan:
                    return Operator.Lt;
                case ExpressionType.LessThanOrEqual:
                    return Operator.Le;
                default:
                    return Operator.Eq;
    

    I can now do this:

    var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId);
    

    I know how brittle this is - it will break if I pass in anything more complex, or even something that looks to be equivalent, like var matchingPeople = Connection.Get<Person>(p => p.MarketId.Equals(marketId));. It does solve 90% of my cases though so I am content to leave it as-is.

    Thanks for this solution. MYSQL: There was 1 error that i fixed, Value should be replaced with Member: var right = binaryExpression.Right.GetType().GetProperty("Member").GetValue(binaryExpression.Right); – user1973285 Mar 16, 2016 at 16:29

    Your function has to return object. The type of p.MarketId == marketId is bool. It therefore needs to be boxed to object, hence the Convert.

    If the expression is always meant to be a predicate, you should change it to:

    Expression<Func<T, bool>> expression
    

    At that point, I'd expect you to see the appropriate binary expression. On the other hand, that then won't work for p => p.MarketId...

    To be honest, it's not really clear what the parameters are meant to mean. It feels like maybe you want two methods - one for a single parameter which is a predicate, and one for two parameters: a projection and a target value.

    Jon, yes that's true; however, I need to pass Expression<Func<T, object>> into Predicates.Field<T>. Is there an elegant way to convert between the two? – Sameer Singh Mar 1, 2013 at 10:06 @SameerSingh: Okay... so why would you expect Get(p => p.MarketId == marketId) to work at all? That would effectively be saying .Where(p => (p.MarketId == marketId) == null) given your value defaulting... that doesn't seem like a good idea to me. – Jon Skeet Mar 1, 2013 at 10:28 Well I made the value parameter optional so that I can switch between var matchingPeople = Connection.Get<Person>(p => p.MarketId, marketId);and var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId);. I will be removing that parameter once I get the latter working (by parsing p => p.MarketId == marketId into its constituent parts). – Sameer Singh Mar 1, 2013 at 10:45 @SameerSingh: As I suggested in my answer, it makes little sense to have a single method when the two different invocation forms mean very different things. Split it out into two different methods now, and get each working individually. – Jon Skeet Mar 1, 2013 at 10:46

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.