풀어 표현식 >

c# dapper extension-methods

문제

DapperDapperExtensions 위에 래퍼 확장 메서드를 만드는 데 바쁩니다 . 현재 LINQ의 Where<T> 확장 메서드와 비슷한 GetList<T> 확장 메서드에 필터링을 추가하려고합니다. 이 질문 을 본 적이 있지만 .NET EqualsExpression 이 .NET 4.5에 없기 때문에 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> 확장 메소드에 특별한주의를 기울이 Get<T> : p => p.MarketId 또는 p => p.MarketId == marketId 중 하나를 전달하면 expression.BodyUnaryExpression 유형입니다. 후자의 경우, expression.Body 실제로 {Convert((p.MarketId == 2))} 합니다.

시도 중

var binaryExpression = expression as BinaryExpression;

null 리턴합니다. 왜냐하면 유용 할 수있는 LeftRight 속성이 있기 때문에 불행합니다.

그래서, 내가 원하는 것을 성취하는 방법을 아는 사람이 있습니까? 더 나아가서 나는 전달 된 람다 표현식을 기반으로 Operator 열거 형을 고를 수 있기를 바랍니다. 어떤 도움이라도 대단히 감사하겠습니다.

수락 된 답변

나는 내가 원하는 것을 성취하는 방법을 알아 냈습니다.

요약하자면:

  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 세 부분으로 분리해야합니다.

@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)); . 그래도 나는 90 %의 사례를 해결하지만 나는 그대로 두는 것이 만족 스럽다.


인기 답변

이게 문제 야:

Expression<Func<T, object>> expression

함수가 object 를 반환해야 object . p.MarketId == marketId 의 유형은 bool 입니다. 따라서하는 박스 될 필요가 object 따라서 Convert .

표현식이 항상 술어 인 경우에는 다음과 같이 변경해야합니다.

Expression<Func<T, bool>> expression

그 시점에서 적절한 바이너리 표현식을 보길 기대합니다. 반면에 p => p.MarketId 는 작동하지 않습니다.

솔직히 매개 변수가 의미하는 바가 무엇인지는 분명하지 않습니다. 투영법과 목표 값 : 술어, 그리고 두 개의 매개 변수에 대해 하나의 단일 매개 변수에 대한 한 - 그것은 아마도 당신은 두 가지 방법을 원하는 것 같은 느낌이 든다.



아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.