拉开表达式<Func >

我正忙于在Dapper和DapperExtensions之上创建包装器扩展方法。 目前我正在尝试向GetList扩展方法添加过滤,类似于LINQ的Where扩展方法。 我已经看到了这个问题,但似乎我无法实现Marc Gravell所建议的,因为.NET 4.5中没有类型EqualsExpression 。 这是一些演示代码,以帮助解释我的问题:

 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(p => p.MarketId, marketId); // This works // Below is a LambdaExpression. expression.Body is, bizarrely, a UnaryExpression with a Convert //var matchingPeople = Connection.Get(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 Get(this IDbConnection connection, Expression<Func> 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> 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(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); } } } 

特别注意我的Get扩展方法:当我传入p => p.MarketIdp => p.MarketId == marketIdexpression.Body的类型为UnaryExpression 。 对于后者, expression.Body实际上包含{Convert((p.MarketId == 2))}

尝试

 var binaryExpression = expression as BinaryExpression; 

返回null ,这是不幸的,因为我可以找到有用的LeftRight属性。

那么,有谁知道如何实现我想要的? 我希望能够根据传入的lambda表达式选择Operator枚举。我们非常感谢任何帮助。

我已经想出如何实现我想要的。

综上所述:

  1. 我需要一个包装DapperExtension的GetList扩展方法的扩展方法。
  2. 后者可以采用IFieldPredicate类型的谓词,我可以使用它来为要执行的SQL查询添加filter。 我可以通过使用Predicates.Field(Expression> expression, Operator op, object value)来实现这一点。
  3. 问题在于将简单的lambda表达式t => t.Id == id转换为Predicates.Field参数。 因此,从概念上讲,我需要将lambda表达式拆分为三个部分: t => t.IdOperator.Eqid

在@ Iridium,@ Eduard和@Jon的帮助下,我的最终解决方案是:

 public static class SqlConnectionExtensions { public static IEnumerable Get(this IDbConnection connection, Expression> expression) where T : class { using (connection) { connection.Open(); var binaryExpression = (BinaryExpression)((UnaryExpression) expression.Body).Operand; var left = Expression.Lambda>(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(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; } } } 

我现在可以这样做:

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

我知道这有多么脆弱 – 如果我传入任何更复杂的内容,或者甚至是看起来相同的内容,它会破坏,例如var matchingPeople = Connection.Get(p => p.MarketId.Equals(marketId)); 。 它确实解决了我90%的情况,所以我满足于保持原样。

这就是问题:

 Expression> expression 

你的函数必须返回objectp.MarketId == marketId的类型是bool 。 因此需要将它装箱到object ,因此Convert

如果表达式始终是谓词,则应将其更改为:

 Expression> expression 

那时,我希望你能看到合适的二进制表达式。 另一方面,那对p => p.MarketId ……

说实话,这些参数意味着什么并不是很清楚。 感觉就像你想要两个方法 – 一个用于单个参数是一个谓词,一个用于两个参数:一个投影和一个目标值。