簡體   English   中英

Dapper IPredicate到linq轉換

[英]Dapper IPredicate to linq conversion

在我的應用程序中,有一些實現一個通用接口的類,我們稱之為IValidator。 實現此類接口的每個類都將返回PredicateGroup對象。 因此,出於測試目的,我決定從數據庫的特定視圖中獲取所有數據,然后在返回的集合(即IEnumerable)上通過linq快速過濾(其中不對具有不同謂詞的數據庫進行多次調用)。

dapper是否支持從IPredicate / PredicateGroup到Func <>的這種轉換,或者還有另一個更快/更好的解決方案?

這是我想要實現的一些演示:

        IEnumerable<Products> products = null;
        using (var cn = new SqlConnection("connectionstring"))
        {
            //Get all elemnts from database(using only one call)
            products = cn.GetList<Products>(Predicates.Field<Products>(f => f.Discontinued, Operator.Eq, true));
        }

        // class which implement IValidator and returns predicate group
        List<IPredicate> computerPredicates = new List<IPredicate>
        {
            Predicates.Field<Products>(f => f.ProductName, Operator.Eq, "Computer"),
            Predicates.Field<Products>(f => f.Price, Operator.Eq, 1200)
        };
        var computerPredicatesGroup = new PredicateGroup {Predicates = computerPredicates };

        // class which implement IValidator and returns predicate group
        List<IPredicate> phonePredicates = new List<IPredicate>
        {
            Predicates.Field<Products>(f => f.ProductName, Operator.Eq, "Phone"),
            Predicates.Field<Products>(f => f.Price, Operator.Eq, 400)
        };
        var phonePredicatesGroup = new PredicateGroup { Predicates = phonePredicates };

        var computers = products.Where( /* computerPredicates */); //??
        var phones = products.Where( /* phonePredicatesGroup */); //??

我沒有找到任何解決方案,所以我決定編寫自己的解析器。

示例如何使用它:

測試模型:

    class TestCarModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Model { get; set; }
        public string Type { get; set; }
        public float Cost { get; set; }
    }

    private List<TestCarModel> PrepareTestData()
    {
        return new List<TestCarModel>()
        {
            new TestCarModel(){ Id = 1, Name = "Fiat", Model = "500", Type = "Kompakt", Cost = 20000 },
            new TestCarModel(){ Id = 2, Name = "Fiat", Model = "Bravo", Type = "Kompakt", Cost = 30000 },
            new TestCarModel(){ Id = 3, Name = "Opel", Model = "Astra", Type = "Sedan", Cost = 20000 },
            new TestCarModel(){ Id = 4, Name = "Honda", Model = "Civic", Type = "Hatchback", Cost = 15000 },
            new TestCarModel(){ Id = 5, Name = "Audi", Model = "A4", Type = "Sedan", Cost = 40000 },
        };
    }

范例1:

var groupPredicate = new PredicateGroup
{
    Operator = GroupOperator.Or,
    Predicates = new List<IPredicate>()
};

var predicateGroup1 = new PredicateGroup
{
    Operator = GroupOperator.Or,
    Predicates = new List<IPredicate>()
    {
        Predicates.Field<TestCarModel>(f => f.Model, Operator.Eq, "500"),
        Predicates.Field<TestCarModel>(f => f.Model, Operator.Eq, "Bravo")
    }
};

var predicateGroup2 = new PredicateGroup
{
    Operator = GroupOperator.Or,
    Predicates = new List<IPredicate>()
    {
        Predicates.Field<TestCarModel>(f => f.Name, Operator.Eq, "Opel"),
        Predicates.Field<TestCarModel>(f => f.Type, Operator.Eq, "Hatchback"),
    }
};

groupPredicate.Predicates.Add(Predicates.Field<TestCarModel>(f => f.Cost, Operator.Gt, 35000));
groupPredicate.Predicates.Add(predicateGroup1);
groupPredicate.Predicates.Add(predicateGroup2);


var testdata = PrepareTestData();
var expression = PredicateParser<TestCarModel>.Parse(groupPredicate).Compile();
var result = testdata.Where(expression);

范例2:

var predicates = new List<IPredicate>
{
    Predicates.Field<TestCarModel>(f => f.Type, Operator.Eq, "Sedan"),
    Predicates.Field<TestCarModel>(f => f.Type, Operator.Eq, "Hatchback"),
};

var testdata = PrepareTestData();
var expression = PredicateParser<TestCarModel>.ParseOr(predicates).Compile();
var result = testdata.Where(expression);

碼:

謂詞分析器

    public static class PredicateParser<T>
    {
        public static Expression<Func<T, bool>> Parse(PredicateGroup predicateGroup)
        {
            var compositeIterator = new CompositeIterator<T>();
            var result = compositeIterator.Prepare(predicateGroup);

            return result;
        }

        public static Expression<Func<T, bool>> ParseAnd(IList<IPredicate> fieldPredicates)
        {
            Expression<Func<T, bool>> finalQuery = t => true;

            foreach (var predicate in fieldPredicates)
            {
                finalQuery = finalQuery.And(Parse(predicate));
            }

            return finalQuery;
        }

        public static Expression<Func<T, bool>> ParseOr(IList<IPredicate> fieldPredicates)
        {
            Expression<Func<T, bool>> finalQuery = t => false;

            foreach (var predicate in fieldPredicates)
            {
                finalQuery = finalQuery.Or(Parse(predicate));
            }

            return finalQuery;
        }

        public static Expression<Func<T, bool>> Parse(IPredicate predicate)
        {
            IFieldPredicate fieldPredicate = (IFieldPredicate)predicate;
            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");
            MemberExpression memberExpression = Expression.PropertyOrField(parameterExpression, fieldPredicate.PropertyName);
            UnaryExpression propertyValue = Expression.Convert(Expression.Constant(fieldPredicate.Value), memberExpression.Type);

            var operatorMatrix = new Dictionary<KeyValuePair<Operator, bool>, Func<Expression>>
            {
                { new KeyValuePair<Operator, bool>(Operator.Like, false),  () => LikeExpression.Like(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Eq, false),    () => Expression.Equal(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Gt, false),    () => Expression.GreaterThan(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Ge, false),    () => Expression.GreaterThanOrEqual(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Lt, false),    () => Expression.LessThan(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Le, false),    () => Expression.LessThanOrEqual(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Like, true),   () => LikeExpression.NotLike(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Eq, true),     () => Expression.NotEqual(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Gt, true),     () => Expression.LessThan(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Ge, true),     () => Expression.LessThanOrEqual(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Lt, true),     () => Expression.GreaterThan(memberExpression, propertyValue) },
                { new KeyValuePair<Operator, bool>(Operator.Le, true),     () => Expression.GreaterThanOrEqual(memberExpression, propertyValue) },
            };

            var body = operatorMatrix[new KeyValuePair<Operator, bool>(fieldPredicate.Operator, fieldPredicate.Not)].Invoke();

            return Expression.Lambda<Func<T, bool>>(body, parameterExpression);
        }
    }

謂詞生成器

    public static class PredicateBuilder
    {
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
            return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
            return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
        }
    }

LikeExpression

    public static class LikeExpression
    {
        private static readonly MethodInfo ApplyLikeMethodInfo = typeof(LikeExpression).GetMethod("ApplyLike");
        private static readonly MethodInfo ApplyLikeNoCaseMethodInfo = typeof(LikeExpression).GetMethod("ApplyLikeNoCase");
        private static readonly MethodInfo ApplyNotLikeMethodInfo = typeof(LikeExpression).GetMethod("ApplyNotLike");
        private static readonly MethodInfo ApplyNotLikeNoCaseMethodInfo = typeof(LikeExpression).GetMethod("ApplyNotLikeNoCase");

        public static Expression Like(Expression lhs, Expression pattern, bool caseSensitive = false)
        {
            return caseSensitive
                ? Expression.Call(ApplyLikeMethodInfo, lhs, pattern)
                : Expression.Call(ApplyLikeNoCaseMethodInfo, lhs, pattern);
        }

        public static Expression NotLike(Expression lhs, Expression pattern, bool caseSensitive = false)
        {
            return caseSensitive
                ? Expression.Call(ApplyNotLikeMethodInfo, lhs, pattern)
                : Expression.Call(ApplyNotLikeNoCaseMethodInfo, lhs, pattern);
        }

        public static bool ApplyLike(string text, string likePattern)
        {
            string pattern = PatternToRegex(likePattern);
            return Regex.IsMatch(text, pattern, RegexOptions.None);
        }

        public static bool ApplyLikeNoCase(string text, string likePattern)
        {
            string pattern = PatternToRegex(likePattern);
            return Regex.IsMatch(text, pattern, RegexOptions.IgnoreCase);
        }

        public static bool ApplyNotLike(string text, string likePattern)
        {
            string pattern = PatternToRegex(likePattern);
            return !Regex.IsMatch(text, pattern, RegexOptions.None);
        }

        public static bool ApplyNotLikeNoCase(string text, string likePattern)
        {
            string pattern = PatternToRegex(likePattern);
            return !Regex.IsMatch(text, pattern, RegexOptions.IgnoreCase);
        }

        public static string PatternToRegex(string pattern)
        {
            pattern = Regex.Escape(pattern);
            pattern = pattern.Replace("%", @".*");
            pattern = $"^{pattern}$";

            return pattern;
        }
    }

CompositeIterator

    public class CompositeIterator<T>
    {
        private Expression<Func<T, bool>> finalQuery;
        private Expression<Func<T, bool>> higherQuery;
        private GroupOperator? previousOperator;
        private int level = -1;

        public Expression<Func<T, bool>> Prepare(PredicateGroup predicateGroup)
        {
            previousOperator = predicateGroup.Operator;
            finalQuery = t => predicateGroup.Operator == GroupOperator.And;
            CallRecursive(predicateGroup);
            return finalQuery;
        }

        private void CallRecursive(PredicateGroup predicateGroup)
        {
            var nodes = predicateGroup.Predicates;
            bool isSet = true;
            ++level;

            foreach (var n in nodes)
            {
                if (n is PredicateGroup @group)
                {                  
                    CallRecursive(@group);
                    --level;
                }
                else
                {
                    var expr = PredicateParser<T>.Parse((IFieldPredicate)n);

                    if (level > 0)
                    {
                        if (isSet)
                        {
                            higherQuery = t => predicateGroup.Operator == GroupOperator.And;
                            isSet = false;
                        }

                        higherQuery = predicateGroup.Operator == GroupOperator.And
                                    ? higherQuery.And(expr)
                                    : higherQuery.Or(expr);
                    }
                    else
                    {
                        previousOperator = predicateGroup.Operator;

                        finalQuery = predicateGroup.Operator == GroupOperator.And
                                    ? finalQuery.And(expr)
                                    : finalQuery.Or(expr);
                    }
                }
            }

            if (higherQuery != null)
            {
                finalQuery = previousOperator == GroupOperator.And
                            ? finalQuery.And(higherQuery)
                            : finalQuery.Or(higherQuery);
            }

            higherQuery = null;
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM