将Alting Expression< Func< T,object> [英] Pulling Apart Expression<Func<T, object>>
问题描述
我正忙于在 Dapper 之上创建包装扩展方法,并且 DapperExtensions 。目前,我正在尝试向 GetList< T>
扩展方法中添加过滤,类似于LINQ的 Where< T>
扩展方法。我已经看到 EqualsExpression $ c .c 4.5中的$ c>。以下是一些演示代码,可帮助解释我的问题:
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);
}
}
}
请特别注意我的 Get< T>
扩展方法:当我传入 p => p.MarketId
或 p => p.MarketId == marketId
,表达式。正文
的类型为 UnaryExpression
。对于后者, expression.Body
实际上包含 {Convert((p.MarketId == 2))}
。
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))}
.
尝试
var binaryExpression = expression as BinaryExpression;
返回 null
,这很不幸,因为那里是我认为有用的左
和右
属性。
returns null
, which is unfortunate because there are Left
and Right
properties which I could have found useful.
那么,有人知道如何实现我想要的吗?再往下走,我希望能够根据传入的lambda表达式选择 Operator
枚举。任何帮助将不胜感激。
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 have figured out how to achieve what I want.
总结:
- 我需要一个扩展方法,该方法包装DapperExtension的
GetList< T>
扩展方法。 - 后者可以接受
IFieldPredicate
类型的谓词,我可以使用该谓词向SQL查询添加过滤器被执行。我可以使用Predicates.Field< T>(Expression< Func< T,object>>表达式,Operator op,object value)
。 - 问题在于转换简单的lambda表达式
t => t.Id == id
放入Predicates.Field< T>
的参数中。因此,从概念上讲,我需要将lambda表达式分为三个部分:t => t.Id
,Operator.Eq
和id
。
- 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 usingPredicates.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 forPredicates.Field<T>
. So, conceptually, I need to pull apart the lambda expression into three parts:t => t.Id
,Operator.Eq
, andid
.
在@ Iridium,@ Eduard和@Jon的帮助下,我的最终解决方案是:
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);
我知道这是多么的脆弱-如果我传递更复杂的东西,甚至其他东西,它都会破裂看起来是等效的,例如 var matchingPeople = Connection.Get< Person>(p => p.MarketId.Equals(marketId));
。不过,它确实解决了我90%的案件,因此我很满意将其保持原样。
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.
这篇关于将Alting Expression< Func< T,object>的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!