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.
–
–
–
–
–
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.
–
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.
–
–
–
–
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.