C# ORM中Dto Linq Expression 和 数据库Model Linq Expression之间的转换

今天在百度知道中看到一个问题,研究了一会便回答了:

http://zhidao.baidu.com/question/920461189016484459.html

如何使dto linq 表达式转换到数据库实体对象linq表达式。自己搜集了一些资料然后实战了一下,还是可行。

自己扩展的一个方法 Cast<TInput, TToProperty>(thisExpression<Func<TInput,bool>> expression),代码如下:

namespace System
{
    public static class LambdaExpressionExtensions
    {
        private static Expression Parser(ParameterExpression parameter, Expression expression)
        {
            if (expression == null) return null;
            switch (expression.NodeType)
            {
                //一元运算符
                case ExpressionType.Negate:
                case ExpressionType.NegateChecked:
                case ExpressionType.Not:
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                case ExpressionType.ArrayLength:
                case ExpressionType.Quote:
                case ExpressionType.TypeAs:
                    {
                        var unary = expression as UnaryExpression;
                        var exp = Parser(parameter, unary.Operand);
                        return Expression.MakeUnary(expression.NodeType, exp, unary.Type, unary.Method);
                    }
                //二元运算符
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                case ExpressionType.Divide:
                case ExpressionType.Modulo:
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.Equal:
                case ExpressionType.NotEqual:
                case ExpressionType.Coalesce:
                case ExpressionType.ArrayIndex:
                case ExpressionType.RightShift:
                case ExpressionType.LeftShift:
                case ExpressionType.ExclusiveOr:
                    {
                        var binary = expression as BinaryExpression;
                        var left = Parser(parameter, binary.Left);
                        var right = Parser(parameter, binary.Right);
                        var conversion = Parser(parameter, binary.Conversion);
                        if (binary.NodeType == ExpressionType.Coalesce && binary.Conversion != null)
                        {
                            return Expression.Coalesce(left, right, conversion as LambdaExpression);
                        }
                        else
                        {
                            return Expression.MakeBinary(expression.NodeType, left, right, binary.IsLiftedToNull, binary.Method);
                        }
                    }
                //其他
                case ExpressionType.Call:
                    {
                        var call = expression as MethodCallExpression;
                        List<Expression> arguments = new List<Expression>();
                        foreach (var argument in call.Arguments)
                        {
                            arguments.Add(Parser(parameter, argument));
                        }
                        var instance = Parser(parameter, call.Object);
                        call = Expression.Call(instance, call.Method, arguments);
                        return call;
                    }
                case ExpressionType.Lambda:
                    {
                        var Lambda = expression as LambdaExpression;
                        return Parser(parameter, Lambda.Body);
                    }
                case ExpressionType.MemberAccess:
                    {
                        var memberAccess = expression as MemberExpression;
                        if (memberAccess.Expression == null)
                        {
                            memberAccess = Expression.MakeMemberAccess(null, memberAccess.Member);
                        }
                        else
                        {
                            var exp = Parser(parameter, memberAccess.Expression);
                            var member = exp.Type.GetMember(memberAccess.Member.Name).FirstOrDefault();
                            memberAccess = Expression.MakeMemberAccess(exp, member);
                        }
                        return memberAccess;
                    }
                case ExpressionType.Parameter:
                    return parameter;
                case ExpressionType.Constant:
                    return expression;
                case ExpressionType.TypeIs:
                    {
                        var typeis = expression as TypeBinaryExpression;
                        var exp = Parser(parameter, typeis.Expression);
                        return Expression.TypeIs(exp, typeis.TypeOperand);
                    }
                default:
                    throw new Exception(string.Format("Unhandled expression type: '{0}'", expression.NodeType));
            }
        }
        public static Expression<Func<TToProperty, bool>> Cast<TInput, TToProperty>(this Expression<Func<TInput, bool>> expression)
        {
            var p = Expression.Parameter(typeof(TToProperty), "p");
            var x = Parser(p, expression);
            return Expression.Lambda<Func<TToProperty, bool>>(x, p);
        }
    }
}

比如有如下的 实体类对象:

    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    public class UserDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

简单的测试代码:

    class Program
    {
        static int[] array0 = new[] { 0, 1 };
        static void Main1(string[] args)
        {
            var array1 = new[] { 0, 1 };
            Expression<Func<UserDto, bool>> exp = null;
            Expression<Func<User, bool>> exp2 = null;

            //====exp====
            //exp = u => u.Name == "张三";
            //exp = u => u.Id.Equals(1);
            //exp = u => u.Id.Equals(1) && u.Name == "张三";
            //exp = u => u.Id.Equals(1) && u.Name == "张三" || u.Name == "张三";
            //exp = u => Filter(u.Name);
            //exp = u => !Filter(u.Name);
            //exp = u => u.Id.Equals(1) && u.Name == "张三" && Filter(u.Name);
            //exp = u => array1.Contains(u.Id);
            //exp = u => array1.Contains(u.Id) || u.Name == "张三";
            //exp = u => array0.Contains(u.Id);
            //exp = u => u.Id > 0;
            //exp = u => u.Id < 10;
            //exp = u => u.Id * 2 < 10;
            //exp = u => u.Id - 2 < 10;
            //exp = u => u.Id + 2 < 10;
            //exp = u => u.Id / 2 < 10;
            //exp = u => (int)(u.Id / 2) < 10;
            //exp = u => u.Name is string;
            //exp = u => ((object)u.Id).ToString() == "1";
            //exp = u => u.Id == default(int);
            //exp = u => true;
            //exp = u => Math.Abs(u.Id)==1;
            exp = u =>
                        u.Id.Equals(1)
                        && u.Name == "张三"
                        && u.Id < 10
                        && array1.Contains(u.Id)
                        && u.Id + 2 < 10
                        && (((object)u.Id).ToString() == "1" || u.Name.Contains("三"))
                        && Math.Abs(u.Id) == 1
                        && Filter(u.Name)
                        && true
                        ;
            //=====exp2=====
            exp2 = exp.Cast<UserDto, User>();
            Console.WriteLine(exp.ToString());
            Console.WriteLine(exp.ToString());



            //测试数据
            List<User> list = new List<User>() { 
                new User{ AAA"},
                new User{ 张三"},
                new User{ 李四"}
            };
            var item = list.Where(exp2.Compile()).FirstOrDefault();
            Console.WriteLine(item.Name);
            Console.ReadKey();
        }

        public static bool Filter(string name)
        {
            return name.Contains("三");
        }
    }

应该说常用的筛选条件都是支持的。这里的list由于没有数据库环境就用List<User>模拟的,真实ORM环境换成list.Where(exp2)就可以了。

性能方面没有测试,应该是可以使用缓存的。有兴趣的朋友可以改一下。