分享

抛弃EF,20分构建一个属于自己的ORM框架(下)

 weijianian 2016-08-09


来源:Poiuyt_cyc

链接:http://www.cnblogs.com/irenebbkiss/p/4157364.html

 

    ///

        /// 根据条件生成对应的sql查询操作符

        ///

        ///

        ///

        private string GetOperator(ExpressionType expressiontype)

        {

            switch (expressiontype)

            {

                case ExpressionType.And:

                    return 'and';

                case ExpressionType.AndAlso:

                    return 'and';

                case ExpressionType.Or:

                    return 'or';

                case ExpressionType.OrElse:

                    return 'or';

                case ExpressionType.Equal:

                    return '=';

                case ExpressionType.NotEqual:

                    return '';

                case ExpressionType.LessThan:

                    return '';

                case ExpressionType.LessThanOrEqual:

                    return '';

                case ExpressionType.GreaterThan:

                    return '>';

                case ExpressionType.GreaterThanOrEqual:

                    return '>=';

                default:

                    throw new Exception(string.Format('不支持{0}此种运算符查找!' + expressiontype));

            }

        }

        private string ResolveFunc(Expression left, Expression right, ExpressionType expressiontype)

        {

            var Name = (left as MemberExpression).Member.Name;

            var Value = (right as ConstantExpression).Value;

            var Operator = GetOperator(expressiontype);

            string CompName = SetArgument(Name, Value.ToString());

            string Result = string.Format('({0} {1} {2})', Name, Operator, CompName);

            return Result;

        }

 

        private string ResolveLinqToObject(Expression expression, object value, ExpressionType? expressiontype = null)

        {

            var MethodCall = expression as MethodCallExpression;

            var MethodName = MethodCall.Method.Name;

            switch (MethodName)//这里其实还可以改成反射调用,不用写switch

            {

                case 'Contains':

                    if (MethodCall.Object != null)

                        return Like(MethodCall);

                    return In(MethodCall, value);

                case 'Count':

                    return Len(MethodCall, value, expressiontype.Value);

                case 'LongCount':

                    return Len(MethodCall, value, expressiontype.Value);

                default:

                    throw new Exception(string.Format('不支持{0}方法的查找!', MethodName));

            }

        }

 

        private string SetArgument(string name, string value)

        {

            name = '@' + name;

            string temp = name;

            while (Argument.ContainsKey(temp))

            {

                int code = Guid.NewGuid().GetHashCode();

                if (code 0)

                    code *= -1;

                temp = name + code;

            }

            Argument[temp] = value;

            return temp;

        }

 

        private string In(MethodCallExpression expression, object isTrue)

        {

            var Argument1 = (expression.Arguments[0] as MemberExpression).Expression as ConstantExpression;

            var Argument2 = expression.Arguments[1] as MemberExpression;

            var Field_Array = Argument1.Value.GetType().GetFields().First();

            object[] Array = Field_Array.GetValue(Argument1.Value) as object[];

            Liststring> SetInPara = new Liststring>();

            for (int i = 0; i )

            {

                string Name_para = 'InParameter' + i;

                string Value = Array[i].ToString();

                string Key = SetArgument(Name_para, Value);

                SetInPara.Add(Key);

            }

            string Name = Argument2.Member.Name;

            string Operator = Convert.ToBoolean(isTrue) ? 'in' : ' not in';

            string CompName = string.Join(',', SetInPara);

            string Result = string.Format('{0} {1} ({2})', Name, Operator, CompName);

            return Result;

        }

 

        private string Like(MethodCallExpression expression)

        {

            object Temp_Vale = (expression.Arguments[0] as ConstantExpression).Value;

            string Value = string.Format('%{0}%', Temp_Vale);

            string Name = (expression.Object as MemberExpression).Member.Name;

            string CompName = SetArgument(Name, Value);

            string Result = string.Format('{0} like {1}', Name, CompName);

            return Result;

        }

 

        private string Len(MethodCallExpression expression, object value, ExpressionType expressiontype)

        {

            object Name = (expression.Arguments[0] as MemberExpression).Member.Name;

            string Operator = GetOperator(expressiontype);

            string CompName = SetArgument(Name.ToString(), value.ToString());

            string Result = string.Format('len({0}){1}{2}', Name, Operator, CompName);

            return Result;

        }

 

    }


static void Main(string[] args)

        {

            string[] Names = { 'Andy', 'Amy', 'Mike' };

            Expressionbool>> func = x => (!Names.Contains(x.Name) & (x.Name == 'A' || x.Name.Count() > 5));

            ResolveExpress resolve = new ResolveExpress();

            resolve.ResolveExpression(func);

            Console.WriteLine(resolve.SqlWhere);

            foreach (var item in resolve.Paras)

            {

                Console.WriteLine(item.ParameterName + ':' + item.Value);

            }

            Console.ReadKey();

        }


结果:



这里有几个重要的东西要给大家讲下


string[] Names={“Andy”,”Amy”,”Mike”};


1.)x => Names.Contains(x.Name);


2.)x => Names.Contains(x.Name)==false;


3.)x => !Names.Contains(x.Name);


这3种在Expression中的表现都不一样


1的话会看成是一个静态方法(MethodCallExpression)


2的话会看成是一个2元运算(BinaryExpression)


3的话会看成是一个1元运算(UnaryExpression)


所以我们都要支持,处理都有所不同。


还有


x=>x.Birthday


string name=”123″;


x=>x.Name==name;



x=>x.Name==”123″


的处理也不一样。大家可以在例子中细细的看看。


这样的构造使得我们切换数据库变得非常简单。因为我们程序中的查询都是基于lambda。换了数据库只要添加一个对应的lamdba转数据库查询条件的实现就可以了。写得够多了。至于数据层怎么封装,到了这一步它已经变得没什么难度了。希望大家能从文章中有所启发和帮助


下篇文章将结合解析Expression和IQueryable来实现延迟加载


补充点东西


IEnumerable和IQueryable有什么不同?


为什么EF查询后返回的是IQueryable而不是IEnumerable。我们对着IQueryableF12去看看。



啥都没,就继承了几个接口。鼠标移到IQueryable上。F12



IQueryable中有3个属性。


Type是类型。


Expresstion是表达式。


那IQueryProvider是什么?


再看看IQueryProvider接口的定义。


CreateQuery是创建查询条件


Execute是执行查询(通常在GetEnumerator()中调用)


当我们IQueryable.Where(x=>x.xxx==”123″)时。其实Where方法内部应该是调用了IQueryable接口中的IQueryProvider属性的CreateQuery(Expresstion expresstion)方法,然后将方法的返回值又返回出来。


而参数(Expresstion )呢?则是IQueryable.Where(x=>x.xxx==”123″)2部分的Expresstion相并。所以IQueryable只是创建条件。所以51楼的朋友说得非常对。


那什么时候执行呢?因为我们的IQueryable继承了IEnumabler,所以我们必须实现GetEnumerator()。我们ToList或foreach时,其实就会调用GetEnumerator()。这时我们就调用Execute进行解析Expresstion,从而得到我们想要的结果。


总结就是IQueryable只是创建条件,当我们调用a.Where(x=>xxx)时,其实是将a与后面的条件相并,生成一个新的IQueryable。当我们foreach时就会调用GetEnumerator()。这时我们一般会调用IQueryProvider里的Execute去解析Expresstion并查询出我们想要的结果


我也知道这篇文章介绍的和我们所说的“ORM”相差很远,但是所谓的ORM最复杂的莫非查询部分了,而依照我这思路走下去,我觉得是可以自己完成一个的。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多