拉开表达式<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.MarketId
或p => p.MarketId == marketId
, expression.Body
的类型为UnaryExpression
。 对于后者, expression.Body
实际上包含{Convert((p.MarketId == 2))}
。
尝试
var binaryExpression = expression as BinaryExpression;
返回null
,这是不幸的,因为我可以找到有用的Left
和Right
属性。
那么,有谁知道如何实现我想要的? 我希望能够根据传入的lambda表达式选择Operator
枚举。我们非常感谢任何帮助。
我已经想出如何实现我想要的。
综上所述:
- 我需要一个包装DapperExtension的
GetList
扩展方法的扩展方法。 - 后者可以采用
IFieldPredicate
类型的谓词,我可以使用它来为要执行的SQL查询添加filter。 我可以通过使用Predicates.Field
来实现这一点。(Expression > expression, Operator op, object value) - 问题在于将简单的lambda表达式
t => t.Id == id
转换为Predicates.Field
参数。 因此,从概念上讲,我需要将lambda表达式拆分为三个部分:t => t.Id
,Operator.Eq
和id
。
在@ 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
。 它确实解决了我90%的情况,所以我满足于保持原样。
这就是问题:
Expression> expression
你的函数必须返回object
。 p.MarketId == marketId
的类型是bool
。 因此需要将它装箱到object
,因此Convert
。
如果表达式始终是谓词,则应将其更改为:
Expression> expression
那时,我希望你能看到合适的二进制表达式。 另一方面,那对p => p.MarketId
……
说实话,这些参数意味着什么并不是很清楚。 感觉就像你想要两个方法 – 一个用于单个参数是一个谓词,一个用于两个参数:一个投影和一个目标值。