2013-03-01 10 views
6

DapperDapperExtensionsの上にラッパー拡張メソッドを作成しています。現時点では、LINQのWhere<T>拡張メソッドと同様に、GetList<T>拡張メソッドにフィルタリングを追加しようとしています。私はthis questionを見てきましたが、.NETの中にEqualsExpressionという型がないので、私はMarc Gravell suggestedを実装できません。ここに私の問題の説明に役立ついくつかのデモコードは次のとおりです。離散式を引き出す<>

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 == marketIdのいずれかを渡すとき、expression.BodyはタイプUnaryExpressionです。後者の場合、expression.Bodyは実際には{Convert((p.MarketId == 2))}を含んでいます。私が有用であることが判明している可能性がLeftRight性質があるので、残念なことです

var binaryExpression = expression as BinaryExpression; 

戻りnullを、しようと

だから、誰も私が望むものを達成する方法を知っていますか?さらにラインを下に私はOperator列挙型に基づいて列挙を選ぶことができるようにしたいと思います。どんな助けも大歓迎です。

+0

私の推測では、 'UnaryExpression'の' Operand'プロパティはあなたが探している 'BinaryExpression'でしょう。 – Iridium

+0

@イリジウムあなたが正しいと思います。デバッグでは、 'expression.Body'の' Operand'が 'LogicalBinaryExpression'型であることがわかりますが、' expression.Body.Operand'のコードにどうやってアクセスすればいいですか?インテリセンスはそれを解決できませんか? –

+0

@Iridiumは絶対に正しいですが、今すぐ試してみましょう: 'BinaryExpression binary =(exparyBinary unaryExpression).Operand as BinaryExpression; 'は、はるかに望ましい' BinaryExpression'を生成します。 @ Jon Skeetは、なぜ変換が現れるのかをあなたに完全に説明しました(値の型は 'object'に囲まれています)...あなた自身が送るより複雑な式のツリーに注意してください。あなたは、プログラマ側で可能性の高いスペクトルを持つ式ツリーパーサーを実行することはできませんし、ライブラリライターの側で非常に大きな前提を作る... –

答えて

5

私は自分が望むものを達成する方法を考え出しました。要約すると

  1. 私はDapperExtensionのGetList<T>拡張メソッドをラップ拡張メソッドが必要です。
  2. 後者は、実行するSQLクエリにフィルタを追加するために使用できるIFieldPredicateという述語を取ります。私はPredicates.Field<T>(Expression<Func<T, object>> expression, Operator op, object value)を使ってこれを達成することができます。
  3. 問題は、簡単なラムダ式t => t.Id == idPredicates.Field<T>のパラメータに変換することにあります。概念的には、ラムダ式をt => t.Id,Operator.Eqidの3つの部分に分割する必要があります。私は今、これを行うことができます

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

    :@Iridium、@Eduardと@ジョンからの助けを借りて

、私の最終的な解決策がある

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

私はこれがどのように脆性を知っています - var matchingPeople = Connection.Get<Person>(p => p.MarketId.Equals(marketId));のように、もっと複雑なもの、あるいは同等のものと思われるものを渡すと、それは壊れます。それは私のケースの90%を解決しますが、私はそれをそのまま残すことに満足しています。

+1

このソリューションをお寄せいただきありがとうございます。 MYSQL:1つのエラーが修正されました.ValueはMemberに置き換えてください。var right = binaryExpression.Right.GetType()。GetProperty( "Member")。GetValue(binaryExpression.Right); –

3

これは問題です:

Expression<Func<T, object>> expression 

あなたの機能はobjectを返すことがあります。 p.MarketId == marketIdのタイプはboolです。したがって、objectにボックス化する必要があります。したがって、Convertです。

式は常に述語をすることを意図されている場合は、あなたがそれを変更する必要があります。その時点で

Expression<Func<T, bool>> expression 

、私はあなたが適切なバイナリ表現を見ることを期待したいです。一方、それではうまくいきませんp => p.MarketId ...

正直言って、パラメータが意味するものは本当に明確ではありません。それは、単一ののパラメータが述語であり、もう1つがの2つの方法:パラメータの2つの方法(投影と目標値)を必要とするように感じます。

+0

ジョン、はい、それは本当です;しかし、私は 'Expression >'をPredicates.Field に渡す必要があります。 2つの間で変換するエレガントな方法はありますか? –

+0

@SameerSingh:「Predicates.Field」がどこから来るのかはっきりしません... –

+0

これはDapperExtensionsライブラリの[Predicates.cs](https://github.com/tmsmith/Dapper-Extensions/blob/master)からのものです/DapperExtensions/Predicates.cs) –

関連する問題