プルーイング式 >

c# dapper extension-methods

質問

私はDapperDapperExtensionsの上にラッパー拡張メソッドを作成するのに忙しいです 。現時点では、LINQのWhere<T>拡張メソッドと同様に、 GetList<T>拡張メソッドにフィルタを追加しようとしています。私はこの質問を見ましたが、.NET 4.5でタイプEqualsExpressionがないので、 Marc Gravellが提案したものを実装できないようです。私の問題の説明に役立ついくつかのデモ・コードがあります:

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いずれかを渡すと、 expression.BodyUnaryExpressionUnaryExpression 。後者の場合、 expression.Bodyには実際に{Convert((p.MarketId == 2))}ます。

試行する

var binaryExpression = expression as BinaryExpression;

null返しnull 。残念ですが、これは私が有用と思っていたLeftプロパティとRightプロパティがあるためです。

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

受け入れられた回答

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

要約すれば:

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

@Iridium、@Eduard、@Jonの助けを借りて、私の最終的な解決策は次のとおりです。

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));ように、より複雑なもの、 var matchingPeople = Connection.Get<Person>(p => p.MarketId.Equals(marketId)); 。それは私のケースの90%を解決しますが、私はそれをそのまま残すことに満足しています。


人気のある回答

これが問題です:

Expression<Func<T, object>> expression

あなたの関数はobjectを返す必要がありobjectp.MarketId == marketIdのタイプはboolです。それゆえ、それはobjectにボックス化される必要があります。したがってConvert

式が常に述語である場合は、次のように変更する必要があります。

Expression<Func<T, bool>> expression

その時点で、私はあなたが適切なバイナリ表現を見ていると思います。一方、それはp => p.MarketId場合は機能しません。

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



ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow