Pulling Apart Expression>

前端 未结 2 727
清歌不尽
清歌不尽 2020-12-30 13:29

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 extension

相关标签:
2条回答
  • 2020-12-30 14:08

    This is the problem:

    Expression<Func<T, object>> expression
    

    Your function has to return object. The type of p.MarketId == marketId is bool. It therefore needs to be boxed to object, hence the Convert.

    If the expression is always meant to be a predicate, you should change it to:

    Expression<Func<T, bool>> expression
    

    At that point, I'd expect you to see the appropriate binary expression. On the other hand, that then won't work for p => p.MarketId...

    To be honest, it's not really clear what the parameters are meant to mean. It feels like maybe you want two methods - one for a single parameter which is a predicate, and one for two parameters: a projection and a target value.

    0 讨论(0)
  • 2020-12-30 14:25

    I have figured out how to achieve what I want.

    In summary:

    1. I need an extension method which wraps DapperExtension's GetList<T> extension method.
    2. 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 using Predicates.Field<T>(Expression<Func<T, object>> expression, Operator op, object value).
    3. The problem lies in transforming a simple lambda expression t => t.Id == id into parameters for Predicates.Field<T>. So, conceptually, I need to pull apart the lambda expression into three parts: t => t.Id, Operator.Eq, and id.

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

    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.

    0 讨论(0)
提交回复
热议问题