DynamicLinq.cs 16.9 KB
// ***********************************************************************
// <copyright file="DynamicLinq.cs" company="">
//     Copyright (c) . All rights reserved.
// </copyright>
// <summary>动态linq</summary>
// ***********************************************************************

using Hh.Mes.Common.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Hh.Mes.Common.Infrastructure
{
    public static class DynamicLinq
    {
        public static ParameterExpression CreateLambdaParam<T>(string name)
        {
            return Expression.Parameter(typeof(T), name);
        }

        /// <summary>
        /// 创建linq表达示的body部分
        /// </summary>
        public static Expression GenerateBody<T>(this ParameterExpression param, Filter filterObj)
        {
            PropertyInfo property = typeof(T).GetProperty(filterObj.Key);

            //组装左边
            Expression left = Expression.Property(param, property);
            //组装右边
            Expression right = null;

            if (
                (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?))
                || (property.PropertyType == typeof(short) || property.PropertyType == typeof(short?))
                || (property.PropertyType == typeof(long) || property.PropertyType == typeof(long?))
                || (property.PropertyType == typeof(decimal) || property.PropertyType == typeof(decimal?))
                || (property.PropertyType == typeof(double) || property.PropertyType == typeof(double?))
            )
            {
                string stype = property.PropertyType.ToString().ToLower();
                Expression leftexp = property.PropertyType.ToString().IndexOf("Nullable") >= 0 ? Expression.Property(left, "Value") : left;
                object filterValue = null;
                if (string.IsNullOrEmpty(filterObj.Value))
                {
                    filterValue = 0;
                }
                else
                {
                    if (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?))
                    { filterValue = int.Parse(filterObj.Value); }
                    else if (property.PropertyType == typeof(short) || property.PropertyType == typeof(short?))
                    { filterValue = short.Parse(filterObj.Value); }
                    else if (property.PropertyType == typeof(long) || property.PropertyType == typeof(long?))
                    { filterValue = long.Parse(filterObj.Value); }
                    else if (property.PropertyType == typeof(decimal) || property.PropertyType == typeof(decimal?))
                    { filterValue = decimal.Parse(filterObj.Value); }
                    else if (property.PropertyType == typeof(double) || property.PropertyType == typeof(double?))
                    { filterValue = double.Parse(filterObj.Value); }
                    //if (stype.IndexOf("int") >= 0)
                    //{ filterValue = int.Parse(filterObj.Value); }
                    //else if (stype.IndexOf("short") >= 0)
                    //{ filterValue = short.Parse(filterObj.Value); }
                    //else if (stype.IndexOf("long") >= 0)
                    //{ filterValue = long.Parse(filterObj.Value); }
                    //else if (stype.IndexOf("decimal") >= 0)
                    //{ filterValue = decimal.Parse(filterObj.Value); }
                    //else if (stype.IndexOf("double") >= 0)
                    //{ filterValue = double.Parse(filterObj.Value); }
                }
                Type type = filterValue.GetType();
                
                left = Expression.Call(leftexp, type.GetMethod("CompareTo", new Type[] { type }),
                                Expression.Constant(filterValue));

                right = Expression.Constant(0);
            }
            else if (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?))
            {
                if (filterObj.Contrast == "like")
                {
                    Expression leftexp = property.PropertyType == typeof(DateTime) ? left : Expression.Property(left, "Value");
                    filterObj.Contrast = "==";
                    if (filterObj.Value.Length == 4)
                    {
                        left = Expression.Call(Expression.Property(leftexp, "Year"), typeof(int).GetMethod("Equals", new Type[] { typeof(int) }),
                                     Expression.Constant(int.Parse(filterObj.Value)));
                    }
                    else if (filterObj.Value.Length == 7)
                    {
                        DateTime date = Convert.ToDateTime(filterObj.Value);
                        left = Expression.Call(Expression.Property(leftexp, "Year"), typeof(int).GetMethod("Equals", new Type[] { typeof(int) }),
                                     Expression.Constant(date.Year)).And(Expression.Call(Expression.Property(leftexp, "Month"), typeof(int).GetMethod("Equals", new Type[] { typeof(int) }),
                                     Expression.Constant(date.Month)));
                    }
                    else
                    {
                        DateTime date = Convert.ToDateTime(filterObj.Value);
                        left = Expression.Call(Expression.Property(leftexp, "Year"), typeof(int).GetMethod("Equals", new Type[] { typeof(int) }),
                                     Expression.Constant(date.Year)).And(Expression.Call(Expression.Property(leftexp, "Month"), typeof(int).GetMethod("Equals", new Type[] { typeof(int) }),
                                     Expression.Constant(date.Month))).And(Expression.Call(Expression.Property(leftexp, "Day"), typeof(int).GetMethod("Equals", new Type[] { typeof(int) }),
                                     Expression.Constant(date.Day)));
                    }
                    right = Expression.Constant(true);
                }
                else
                {
                    Expression leftexp = property.PropertyType == typeof(DateTime) ? left : Expression.Property(left, "Value");
                    object filterValue = string.IsNullOrEmpty(filterObj.Value) ? DateTime.Now : DateTime.Parse(filterObj.Value);

                    left = Expression.Call(leftexp, typeof(DateTime).GetMethod("CompareTo", new Type[] { typeof(DateTime) }),
                                    Expression.Constant(filterValue));

                    right = Expression.Constant(0);
                }
            }
            else if (property.PropertyType == typeof(string))
            {
                filterObj.Value = filterObj.Value.ToUpper();
                left = Expression.Call(left, typeof(string).GetMethod("ToUpper", new Type[] { }));
                right = Expression.Constant((filterObj.Value));
            }
            else if (property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?))
            {
                if (!string.IsNullOrEmpty(filterObj.Value))
                {
                    if (filterObj.Value.ToLower().Equals("true") || filterObj.Value.ToLower().Equals("1"))
                    {
                        filterObj.Value = "true";
                    }
                    else
                    {
                        filterObj.Value = "false";
                    }
                }

                Expression leftexp = property.PropertyType == typeof(bool) ? left : Expression.Property(left, "Value");
                object filterValue = string.IsNullOrEmpty(filterObj.Value) ? bool.Parse("true") : bool.Parse(filterObj.Value);

                left = Expression.Call(leftexp, typeof(bool).GetMethod("CompareTo", new Type[] { typeof(bool) }),
                             Expression.Constant(filterValue));

                right = Expression.Constant(0);
            }
            else if (property.PropertyType == typeof(Guid) || property.PropertyType == typeof(Guid?))
            {
                Expression leftexp = property.PropertyType == typeof(Guid) ? left : Expression.Property(left, "Value");
                object filterValue = string.IsNullOrEmpty(filterObj.Value) ? Guid.Parse("00000000-0000-0000-0000-000000000000") : Guid.Parse(filterObj.Value);

                left = Expression.Call(leftexp, typeof(Guid).GetMethod("CompareTo", new Type[] { typeof(Guid) }),
                             Expression.Constant(filterValue));

                right = Expression.Constant(0);
            }
            else
            {
                throw new Exception("暂不能解析该Key的类型");
            }

            CheckFilterString(filterObj.JqContrast, property.PropertyType);

            //c.XXX=="XXX"
            Expression filter = Expression.Equal(left, right);//等于
            switch (filterObj.Contrast)
            {
                case "<="://小于等于
                    filter = Expression.LessThanOrEqual(left, right);
                    break;

                case "<"://小于
                    filter = Expression.LessThan(left, right);
                    break;

                case ">"://大于
                    filter = Expression.GreaterThan(left, right);
                    break;

                case ">="://大于等于
                    filter = Expression.GreaterThanOrEqual(left, right);
                    break;
                case "!="://不等于
                    filter = Expression.NotEqual(left, right);
                    break;

                case "like"://包含
                    filter = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),
                                 Expression.Constant(filterObj.Value));
                    break;
                case "not like"://不包含
                    filter = Expression.Not(Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),
                                 Expression.Constant(filterObj.Value)));
                    break;
                case "in"://属于
                    var lExp = Expression.Constant(filterObj.Value.Split(',').ToList()); //数组
                    var methodInfo = typeof(List<string>).GetMethod("Contains", new Type[] { typeof(string) }); //Contains语句
                    filter = Expression.Call(lExp, methodInfo, left);
                    break;
                case "not in"://不属于
                    var listExpression = Expression.Constant(filterObj.Value.Split(',').ToList()); //数组
                    var method = typeof(List<string>).GetMethod("Contains", new Type[] { typeof(string) }); //Contains语句
                    filter = Expression.Not(Expression.Call(listExpression, method, left));
                    break;
                case "begin with"://开始于
                    filter = Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }),
                                 Expression.Constant(filterObj.Value));
                    break;
                case "not begin with"://不开始于
                    filter = Expression.Not(Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }),
                                 Expression.Constant(filterObj.Value)));
                    break;
                case "end with"://结束于
                    filter = Expression.Call(left, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }),
                                 Expression.Constant(filterObj.Value));
                    break;
                case "not end with"://不结束于
                    filter = Expression.Not(Expression.Call(left, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }),
                                 Expression.Constant(filterObj.Value)));
                    break;
                case "null"://为空
                    filter = Expression.Equal(left, Expression.Constant(string.Empty));
                    break;
                case "not null"://不为空
                    filter = Expression.NotEqual(left, Expression.Constant(string.Empty));
                    break;
            }

            return filter;
        }

        private static void CheckFilterString(string p, Type type)
        {
            bool valid = false;
            string errmsg = string.Format("{0}类型不支持{1}操作", type, p);
            string typeS = type.ToString();
            if (type == typeof(int) || type == typeof(int?) || type == typeof(decimal) || type == typeof(decimal?) || type == typeof(long) || type == typeof(long?) || type == typeof(short) || type == typeof(short?) || type == typeof(double) || type == typeof(double?) || type == typeof(DateTime) || type == typeof(DateTime?))
            {
                if (type == typeof(DateTime) || type == typeof(DateTime?))
                {
                    valid = ("eq,ne,lt,le,gt,ge,cn".Split(',').Contains(p));
                }
                else
                {
                    valid = ("eq,ne,lt,le,gt,ge".Split(',').Contains(p));
                }
            }
            else if (type == typeof(string))
            {
                valid = ("eq,ne,bw,bn,in,ni,ew,en,cn,nc,nu,nn".Split(',').Contains(p));
            }
            else if (type == typeof(Guid) || type == typeof(Guid?) || type == typeof(bool) || type == typeof(bool?))
            {
                valid = ("eq,ne".Split(',').Contains(p));
            }

            if (!valid) { throw new Exception(errmsg); };
        }

        public static Expression<Func<T, bool>> GenerateTypeBody<T>(this ParameterExpression param, Filter filterObj)
        {
            return (Expression<Func<T, bool>>)(param.GenerateBody<T>(filterObj));
        }

        /// <summary>
        /// 创建完整的lambda
        /// </summary>
        public static LambdaExpression GenerateLambda(this ParameterExpression param, Expression body)
        {
            //c=>c.XXX=="XXX"
            return Expression.Lambda(body, param);
        }

        public static Expression<Func<T, bool>> GenerateTypeLambda<T>(this ParameterExpression param, Expression body)
        {
            return (Expression<Func<T, bool>>)(param.GenerateLambda(body));
        }

        public static Expression AndAlso(this Expression expression, Expression expressionRight)
        {
            return Expression.AndAlso(expression, expressionRight);
        }

        public static Expression Or(this Expression expression, Expression expressionRight)
        {
            return Expression.Or(expression, expressionRight);
        }

        public static Expression And(this Expression expression, Expression expressionRight)
        {
            return Expression.And(expression, expressionRight);
        }

        //系统已经有该函数的实现
        //public static IQueryable<T> Where<T>(this IQueryable<T> query, Expression expression)
        //{
        //    Expression expr = Expression.Call(typeof(Queryable), "Where", new[] { typeof(T) },
        //       Expression.Constant(query), expression);
        //    //生成动态查询
        //    IQueryable<T> result = query.Provider.CreateQuery<T>(expr);
        //    return result;
        //}

        public static IQueryable<T> GenerateFilter<T>(this IQueryable<T> query, string filterjson)
        {
            if (!string.IsNullOrEmpty(filterjson) && filterjson != "null")
            {
                var filters = JsonHelper.Instance.Deserialize<IEnumerable<Filter>>(filterjson);
                var param = CreateLambdaParam<T>("c");

                Expression result = Expression.Constant(true);
                foreach (var filter in filters)
                {
                    result = result.AndAlso(param.GenerateBody<T>(filter));
                }

                query = query.Where(param.GenerateTypeLambda<T>(result));
            }
            return query;
        }

        public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
        {
            var parameter = Expression.Parameter(typeof(T));

            var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
            var left = leftVisitor.Visit(expr1.Body);

            var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
            var right = rightVisitor.Visit(expr2.Body);

            return Expression.Lambda<Func<T, bool>>(
                Expression.AndAlso(left, right), parameter);
        }

        private class ReplaceExpressionVisitor : ExpressionVisitor
        {
            private readonly Expression _oldValue;
            private readonly Expression _newValue;

            public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
            {
                _oldValue = oldValue;
                _newValue = newValue;
            }

            public override Expression Visit(Expression node)
            {
                if (node == _oldValue)
                    return _newValue;
                return base.Visit(node);
            }
        }
    }
}