瑞雪年

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: :: 订阅 订阅 :: 管理 ::

   我们知道的在.net cf 2.0上没有对LINQ的支持,在.net cf 3.5上即使支持Linq To DataSet和Linq To XML,也还是没有Linq To SQL,在可能即将发布的.net cf 4.0中,好像也没提到Linq To SQL什么事儿,那么是不是基于.net cf 的开发就与Linq To SQL无缘呢?!基实也不尽然。
     在前天我写过一篇《在 .NET 2.0上使用“LINQ”》中,提到通过移植mono代码,在.net 2.0上实现LINQ,那么能不能把它也移植到.net cf上哪?说干就干,跟随我一起来一次“冒险”吧!
第一关:Reflection.Emit
    在.net cf 2.0 及 3.5上,没有Reflection.Emit相关类库,然而,LINQ Expression的Compile离可它就玩不转了。不过,很幸运地,我们可以找到个EmitCF项目,它基本上实现了Linq Expression用到的功能,第一关算过了。
第二关:GetCurrentMethod
    精简版就是精简版,经常会碰到函数缺失,多数比较简单的函数基本可以自已实现,可是,一但碰到“内核级”的函数,本人就无能为力了,不可硬攻,就来点“旁门左道”,网上比较流行的方法是通过在Stack中查找最近一次函数调用,可是这种方法无法区分同名的函数,我们不能用,又经过一通琢磨,我发现可以通过函数参数名来区分不同的函数,这一关也过了。

        private static Dictionary<string, Dictionary<string, MethodInfo>> methods = null;

        private static void InitializeMethods()

        {

            methods = new Dictionary<string, Dictionary<string, MethodInfo>>();

            MethodInfo[] methodInfoArray = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public);

            foreach (var methodInfo in methodInfoArray)

            {

                string methodName = methodInfo.Name;

                if (!methods.ContainsKey(methodName))

                {

                    methods.Add(methodName, new Dictionary<string, MethodInfo>());

                }

                Dictionary<string, MethodInfo> methodDictionary = methods[methodName];

                string[] parameterNames = GetParameterNames(methodInfo);

                string parameterNameKey = string.Join(",", parameterNames);

                if (!methodDictionary.ContainsKey(parameterNameKey))

                {

                    methodDictionary.Add(parameterNameKey, methodInfo);

                }

            }

        }



        private static string[] GetParameterNames(MethodInfo methodInfo)

        {

            if (methodInfo.IsGenericMethodDefinition)

            {

                System.Type[] genericArguments = methodInfo.GetGenericArguments();

                System.Type[] types = new System.Type[genericArguments.Length];

                for (int i = 0; i < types.Length; i++)

                {

                    types[i] = typeof(int);

                }

                methodInfo = methodInfo.MakeGenericMethod(types);

            }

            ParameterInfo[] parameterInfoArray = methodInfo.GetParameters();

            string[] parameterNames = new string[parameterInfoArray.Length];

            for (int i = 0; i < parameterInfoArray.Length; i++)

            {

                parameterNames[i] = parameterInfoArray[i].Name;

            }

            return parameterNames;

        }



        private static MethodBase GetCurrentMethod(string methodName, string[] parameterNames)

        {

            if (methods == null)

            {

                InitializeMethods();

            }

            if (methods.ContainsKey(methodName))

            {

                Dictionary<string, MethodInfo> methodDictionary = methods[methodName];

                string parameterNameKey = string.Join(",", parameterNames);

                if (methodDictionary.ContainsKey(parameterNameKey))

                {

                    return methodDictionary[parameterNameKey];

                }

            }

            return null;

        }

第三关:inner class
    前面提到的EmitCF有个问题,因为它生成独立的程序集来执行Emit的动态代码,而Linq表达式内的支匿名对象又被编译成inner类型,这样EmitCF生成的动态代码就访问不到这些对象,这种情况下我们通过会第一时间想到"反射",那我们就用Emit生成反射调用代码来访问这些对象,调试起来确实麻烦了许多,不过终归还是执行过去了。
第四关:DbLinq
  说到这儿,离LINQ TO SQL很近了,但也不就说,这样就很容易现实,因为我想移植DbLinq过程中可能还会碰到更多问题,很遗憾我没机会走下去了,因为我最初的目的是要完成.net cf 上的"Linq to DAC",我的想法基本上实现了,虽然还有些问题(见注1),但今天也就到这儿了。

 

        [Test]
        public void TestMethod1()
        {
            QueryableData<Issue> issues = new QueryableData<Issue>(dc);
             var il = from s in issues where s.IssueID > 0 && s.Title == "a" select s;
            foreach (var i in il)
            {
                Assert.IsTrue(i.IssueID > 0);
                Assert.IsTrue(i.Title == "a");
            }
        }


注1:
1. 动态程序集没法的内存中直接加载,一定要生成物理文件。
2. 在Linq表达式中,不能使用数组的元素作为数据值,如:s.IssueID == issueList[0], 会出错。
3. 不能使用select new {...} 返回新的动态对象。

posted on 2010-04-11 14:34  瑞雪年  阅读(1429)  评论(0编辑  收藏  举报