2013-03-01 48 views
6

我正忙於在DapperDapperExtensions上創建封裝擴展方法。此刻,我正在嘗試將過濾添加到GetList<T>擴展方法中,類似於LINQ的Where<T>擴展方法。我看過this question,但似乎我無法實現什麼Marc Gravell suggested,因爲.NET 4.5中沒有類型EqualsExpression。下面是一些演示代碼,以幫助我的問題的解釋:拉開表達式<Func <T, object>>

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.MarketIdp => p.MarketId == marketIdexpression.BodyUnaryExpression型。對於後者,expression.Body實際上包含{Convert((p.MarketId == 2))}

試圖

var binaryExpression = expression as BinaryExpression; 

回報null,因爲有它,我可以找到有用LeftRight特性,這是不幸的。

那麼,有沒有人知道如何實現我想要的?更進一步,我希望能夠根據傳入的lambda表達式來選擇Operator枚舉。任何幫助將非常感謝。

+0

我的猜測是'UnaryExpression'的'Operand'屬性是'BinaryExpression'你正在尋找。 – Iridium 2013-03-01 09:53:36

+0

@Iridium我認爲你是對的;通過調試,我可以看到'expression.Body'的'Operand'類型爲'LogicalBinaryExpression',但是如何訪問'expression.Body.Operand' in-code?智能感知無法解決這個問題? – 2013-03-01 10:00:47

+0

@Iridium是絕對正確的,現在就試試吧:'BinaryExpression binary =(expr.Body as UnaryExpression).Operand as BinaryExpression;'產生期望的'BinaryExpression'。 @Jon Skeet完全向你解釋爲什麼轉換會出現(值類型被裝箱到'object')......當心你自己可能發送的更復雜的表達式樹。你不能在用戶程序員一方做一個表達式樹解析器,並在庫書寫器端做出非常大的假設...... – 2013-03-01 10:04:14

回答

5

我已經想出瞭如何實現我想要的。

總結:

  1. 我需要它包裝DapperExtension的GetList<T>擴展方法擴展方法。
  2. 後者可能會採用IFieldPredicate類型的謂詞,我可以使用該謂詞將過濾器添加到要執行的SQL查詢中。我可以通過使用Predicates.Field<T>(Expression<Func<T, object>> expression, Operator op, object value)來實現此目的。
  3. 問題在於將簡單的lambda表達式t => t.Id == id轉換爲Predicates.Field<T>的參數。因此,從概念上講,我需要將拉姆達表達式分解爲三部分:t => t.Id,Operator.Eqid

與@Iridium,@Eduard和@喬恩的幫助下,我最終的解決方案是:

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; 
     } 
    } 
} 

我現在可以這樣做:

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

我知道如何脆弱,這是 - 如果我傳入更復雜的東西,甚至看起來相當的東西,它會中斷,如var matchingPeople = Connection.Get<Person>(p => p.MarketId.Equals(marketId));。它確實解決了我90%的案例,儘管如此,我滿足於保持原樣。

+1

感謝您的解決方案。 MYSQL:我修正了1個錯誤,值應該用成員替換:var right = binaryExpression.Right.GetType()。GetProperty(「Member」)。GetValue(binaryExpression.Right); – 2016-03-16 16:29:32

3

這就是問題所在:

Expression<Func<T, object>> expression 

你的函數必須返回objectp.MarketId == marketId的類型是bool。因此它需要被裝箱到object,因此Convert

如果表達式總是意味着是一個謂語,你應該將其更改爲:

Expression<Func<T, bool>> expression 

在這一點上,我希望你能看到相應的二進制表示。另一方面,那麼p => p.MarketId將不起作用...

說實話,這些參數意味着什麼都不是很清楚。感覺也許你想要兩種方法 - 一個是單個參數,這是一個謂詞,另一個參數是兩個參數:投影和目標值。

+0

喬恩,是的,這是真的;然而,我需要將'Expression >'傳遞到Predicates.Field 。有沒有一種優雅的方式可以在兩者之間進行轉換? – 2013-03-01 10:06:41

+0

@SameerSingh:那麼不清楚'Predicates.Field'甚至來自哪裏... – 2013-03-01 10:19:39

+0

這是來自DapperExtensions庫,[Predicates.cs](https://github.com/tmsmith/Dapper-Extensions/blob/master /DapperExtensions/Predicates.cs) – 2013-03-01 10:22:37

相關問題