jeudi 25 juin 2015

c# Predicate Builder for dynamic Objects

I have a home made library that creates expresions used in filetring data in grids ui elemen this is basic method:

public static Expression<Func<T, bool>> GetPredicate<T>( String modelPropertyName, SearchType searchType, object data) 

It's really simple to query for objects. Basicaly you need only property name, operator and data to compare. It's using reflection to create Expressions for each operator.

And now I have requirement to extend this functionality.... This method uses Type T as you can see.Now they want me to change it and pass Type as an argument so new method should be something like:

public static Expression<Func<object, bool>> GetPredicate(Type T , String modelPropertyName, SearchType searchType, object data) 

The don't know how many propperties will object have. Also They want tu use "dynamic" objects and ExpandoObjects. Well that sucks.

I don't know if I can get anonymous Type at runtime from ExpandObject. I don't know also if I can return

Expression<Func<object, bool>>

and apply this expression to dynamic object. For sure it can't be used within linq to sql, but even within linq to Objects it's hard to accomplish.

In fact they(devs from my team btw) are considering using Dictionaries.

Could anyone point me maybe some library that can do this? Or maybe the way I shodl start with this?

this is entire class ( very usefull BTW)

namespace ###########################
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;

    public static class PredicateBuilder
    {
        #region Public  Static Methods

        /// <summary>
        ///  This is main Method of this Class It returns predicate
        ///  
        /// </summary>
        /// <typeparam name="T">Entity Type - Search Where</typeparam>
        /// <param name="modelPropertyName">property to compare (compare what)(can be nested property)</param>
        /// <param name="searchType">comparation Type (compare how)</param>
        /// <param name="data">data to compare (compare to what )</param>
        /// <returns>Able to translate to SQl predicate</returns>
        public static Expression<Func<T, bool>> GetPredicate<T>(String modelPropertyName, SearchType searchType, object data) where T : class
        {
            ParameterExpression parameterExp = Expression.Parameter(typeof(T), "t");
            MemberExpression member = Expression.PropertyOrField(parameterExp, modelPropertyName.Split('.').First());

            // If there are any dots in parram then we have to change expression 
            foreach (var innerMember in modelPropertyName.Split('.').Skip(1))
            {
                member = Expression.PropertyOrField(member, innerMember);
            }

            if (member.Type.BaseType.ToString() == "System.Enum")
            {
                data = Int32.Parse(data.ToString());
                String name = Enum.GetName(member.Type, data);
                data = Enum.Parse(member.Type, name, false);
            }
            else if (searchType != SearchType.IsIn)
            {
                switch (member.Type.ToString())
                {
                    case "System.Nullable`1[System.Int32]":
                        data = data.ToString().ToNullableInt32();
                        break;

                    case "System.Nullable`1[System.Boolean]":
                        data = data.ToString().ToNullableBoolean();
                        break;

                    case "System.Boolean":
                        data = Boolean.Parse(data.ToString());
                        break;

                    case "System.Nullable`1[System.DateTime]":
                        data = data.ToString().ToNullableDateTime();
                        break;

                    case "System.DateTime":
                        data = DateTime.Parse(data.ToString());
                        break;

                    case "System.Int32":
                        data = Int32.Parse(data.ToString());
                        break;
                }
            }
            ConstantExpression valuetoCheck;

            if (searchType == SearchType.IsIn)
            {
                valuetoCheck = Expression.Constant(data, GetListType(member.Type));
            }
            else
            {
                valuetoCheck = Expression.Constant(data, member.Type);
            }

            Expression expression = getExpression<T>(searchType, member, valuetoCheck);

            Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { parameterExp });
            return predicate;
        }

        private static Expression getExpression<T>(SearchType searchType, MemberExpression member, ConstantExpression valuetoCheck) where T : class
        {
            Expression expression;
            switch (searchType)
            {
                case SearchType.Equal:
                    expression = Equals<T>(member, valuetoCheck);
                    break;

                case SearchType.NotEqual:
                    expression = NotEquals<T>(member, valuetoCheck);
                    break;

                case SearchType.Less:
                    expression = Less<T>(member, valuetoCheck);
                    break;

                case SearchType.LessOrEqual:
                    expression = LessOrEqual<T>(member, valuetoCheck);
                    break;

                case SearchType.Greater:
                    expression = More<T>(member, valuetoCheck);
                    break;

                case SearchType.GreaterOrEqual:
                    expression = MoreorEqual<T>(member, valuetoCheck);
                    break;

                case SearchType.BeginsWith:
                    expression = BeginsWith<T>(member, valuetoCheck);
                    break;

                case SearchType.DoesNotBeginWith:
                    expression = NotBeginsWith<T>(member, valuetoCheck);
                    break;

                case SearchType.IsIn:
                    expression = IsIn<T>(member, valuetoCheck);
                    break;

                case SearchType.IsNotIn:
                    expression = NotContains<T>(member, valuetoCheck);
                    break;

                case SearchType.EndsWith:
                    expression = EndsWith<T>(member, valuetoCheck);
                    break;

                case SearchType.DoesNotEndWith:
                    expression = NotEndsWith<T>(member, valuetoCheck);
                    break;

                case SearchType.Contains:
                    expression = Contains<T>(member, valuetoCheck);
                    break;

                case SearchType.DoesNotContain:
                    expression = NotContains<T>(member, valuetoCheck);
                    break;

                case SearchType.IsNull:
                    expression = IsNull<T>(member, valuetoCheck);
                    break;

                case SearchType.IsNotNull:
                    expression = IsNotNull<T>(member, valuetoCheck);
                    break;

                default:
                    expression = Expression<Func<T, bool>>.Equal(member, valuetoCheck);
                    break;
            }
            return expression;
        }

        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.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>
                  (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
        }

        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.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>
                  (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
        }

        public static Expression<Func<T, bool>> False<T>()
        {
            return f => false;
        }

        public static Expression<Func<T, bool>> True<T>()
        {
            return f => true;
        }

        public static IList CreateList(Type type)
        {
            Type genericListType = typeof(List<>).MakeGenericType(type);
            return ((IList)Activator.CreateInstance(genericListType));
        }

        public static Type GetListType(Type type)
        {
            return CreateList(type).GetType();
        }

        #endregion Public  Static Methods

        #region predicateExpressions

        private static Expression BeginsWith<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            MethodInfo method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
            return Expression<Func<T, bool>>.Call(member, method, valuetoCheck);
        }

        private static Expression Contains<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {

            MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            return Expression<Func<T, bool>>.Call(member, method, valuetoCheck);
        }

        private static Expression IsIn<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            MethodInfo method = GetListType(member.Type).GetMethod("Contains", new[] { member.Type }); 
            return Expression<Func<T, bool>>.Call(valuetoCheck, method, member);
        }

        private static Expression EndsWith<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            MethodInfo method = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
            return Expression<Func<T, bool>>.Call(member, method, valuetoCheck);
        }

        private static Expression Equals<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            return Expression<Func<T, bool>>.Equal(member, valuetoCheck);
        }

        private static Expression IsNotNull<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            return Expression<Func<T, bool>>.NotEqual(member, Expression.Constant(null, member.Type));
        }

        private static Expression IsNull<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            return Expression<Func<T, bool>>.Equal(member, Expression.Constant(null, member.Type));
        }

        private static Expression Less<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            return Expression<Func<T, bool>>.LessThan(member, valuetoCheck);
        }

        private static Expression LessOrEqual<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            return Expression<Func<T, bool>>.LessThanOrEqual(member, valuetoCheck);
        }

        private static Expression More<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            return Expression<Func<T, bool>>.GreaterThan(member, valuetoCheck);
        }

        private static Expression MoreorEqual<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            return Expression<Func<T, bool>>.GreaterThanOrEqual(member, valuetoCheck);
        }

        private static Expression NotBeginsWith<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            MethodInfo method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
            return Expression.Not(Expression<Func<T, bool>>.Call(member, method, valuetoCheck));
        }

        private static Expression NotContains<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            return Expression.Not(Expression<Func<T, bool>>.Call(member, method, valuetoCheck));
        }

        private static Expression NotEndsWith<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            MethodInfo method = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
            return Expression.Not(Expression<Func<T, bool>>.Call(member, method, valuetoCheck));
        }

        private static Expression NotEquals<T>(MemberExpression member, ConstantExpression valuetoCheck)
        {
            return Expression<Func<T, bool>>.NotEqual(member, valuetoCheck);
        }

        #endregion predicateExpressions

        #region Pivate static
        private static Boolean? ToNullableBoolean(this string s)
        {
            bool i;
            if (Boolean.TryParse(s, out i)) return i;
            return null;
        }

        private static DateTime? ToNullableDateTime(this string s)
        {
            DateTime i;
            if (DateTime.TryParse(s, out i)) return i;
            return null;
        }

        private static int? ToNullableInt32(this string s)
        {
            int i;
            if (Int32.TryParse(s, out i)) return i;
            return null;
        }
        #endregion
    }
}





Aucun commentaire:

Enregistrer un commentaire