×

Loading...
Ad by
  • 推荐 OXIO 加拿大高速网络,最低月费仅$40. 使用推荐码 RCR37MB 可获得一个月的免费服务
Ad by
  • 推荐 OXIO 加拿大高速网络,最低月费仅$40. 使用推荐码 RCR37MB 可获得一个月的免费服务

学习笔记:LINQ 查询的一些区别

本文发表在 rolia.net 枫下论坛我贴的这些都是我平时的学习笔记,过一段时间这些知识会忘记,我希望在我还清醒的时候把这些玩意以尽量通俗易懂的方式记录下来,以备不测:

Linq 有两种查询,完全按照资料翻译就是“本地查询”Local Query 和“翻译查询”Interpreted Query,听着很令人困惑,没办法,英文原文如此。这两种查询很多程序员往往不加区别的使用,在大多数情况下没有任何问题,两种查询中所暴露出来的使用方法是一模一样的。就像我开QQ车和开Toyota车,暴露给驾驶员的开车方法没有任何不同,都是油门刹车,但是QQ直接把刹车力度的大小传递到刹车鼓上,而Toyota会把刹车力度大小首先送给内部计算机,计算机“翻译”一下,再决定是应该自动爆冲还是立即刹车。Linq的2种查询有些像我说的这个例子

半年多前我在使用Linq查询的时候遇到了一个异常错误,在解决问题的时候理解了这两种查询的不同之处,我出错的程序是这样的:

我有一个对数据库表操作的(Linq to SQL)查询,表名是student, fields 分别是
create table student
(
name varchar(20),
age int,
address varchar(20)
)

在C#中,这个表的定义类是这样写的:

[Table]
public student
{
[Column]
public string name;
[Column]
public int age;
[Column]
public string address;

public student(string n) { name = n; }
}

我要找出所有 name 符合某种Regular Express的student,理想的写法应该是这样的
main()
{
DataContext dc = new DataContext("connection string");
Table<student> students = dc.GetTable<student>();
// 找出所有name以k开头,以p结尾的student
var result = students.Where(s=>Regex.IsMatch(s.name,"^k.*p$"));
foreach(student s in result) Console.WriteLine(s);
}

程序运行时出错,提示T-SQL没有相应的函数,我把程序改成这个样子
// 找出所有name以k开头,以p结尾的student
var result = students.Where(s=>s.name.StartWith("k") && s.name.EndWith("p"));

程序运行正常,呵呵,区别原来在这里
student.Where(....)返回的是这样一个东西

IQueryable<student> result = students.Where(s=>Regex.IsMatch(s.name,"^k.*p$"));

一旦返回值是IQueryable<T>,那么这样的查询就会被称为是“翻译查询”。“本地查询”所返回的值永远是IEnumerable<T>,比如这个例子
var result = (new student[] {new student("Bush"),new student("Tom"),new student("kacp")}).Where(s=>Regex.IsMatch(s.name,"^k.*p$"));

同样的类型,同样的student,只不过我在本地把它封装成一个数组,在用Where查询的时候,它的返回值就是一个IEnumerable<student>的值,完整的写出是这个样子:
IEnumerable<student> result = (new student[] {new student("Bush"),new student("Tom"),new student("kacp")}).Where(s=>Regex.IsMatch(s.name,"^k.*p$"));
这个查询就是所谓的“本地查询”,而且这个本地查询不会出错,正则表达试运行得非常好

看看区别,首先从语法定义上看:
本地查询:public static IEnumerable<TSource> Where<TSource> (this
IEnumerable<TSource> source, Func<TSource,bool> predicate)

翻译查询:public static IQueryable<TSource> Where<TSource> (this
IQueryable<TSource> source, Expression<Func<TSource,bool>> predicate)

最大的区别在于
本地查询:Func<TSource,bool> predicate 和
翻译查询:Expression<Func<TSource,bool>> predicate

本地查询的表达式会在编译的时候直接被编译器编译成委托变成.dll或者.exe加以运行,但是翻译查询的表达式首先会变成一个“表达式树”(Expression<T>)静静的躺在程序里面,直到运行时这个“表达式树”才会被“翻译”成对象能够理解的“查询”(比如T-SQL,或者在Entity Framework里是一种特殊的对Entity Data Model进行查询的SQL)加以运行,是不折不扣地“动态”运行,这也就是为什么我的Linq to SQL 的 Where查询在编译的时候不会出错,因为编译器不理会你要做什么,只是帮助你检查你的C#“查询”语法有没有错误,而这个查询到底能不能用直到运行时才会知道。

那么是谁负责“翻译”我的“表达式树 查询”呢?在我的例子中,是DataContext类,它里面有一套程序专门负责在运行时把我传递给它的“表达式树 查询”[在这个例子中是:.Where(s=>Regex.IsMatch(s.name,"^k.*p$")]翻译成数据库能够听得懂的语言,当然就是T-SQL了,然后运行这个T-SQL,在把返回结果变成IQueryable<T>的形式返回给.net。

现在我的这个错误就很好理解了,因为T-SQL中没有Regular Express的操作,所以DataContext无法把我的查询请求翻译成T-SQL语言,于是就会报错。而我的另外一种查询
students.Where(s=>s.name.StartWith("k") && s.name.EndWith("p"));
DataContext里面有相应的功能把它们翻译成T-SQL运行,所以就运行正常啦(我想理论上微软如果愿意肯定也能支持正则表达式,可能LINQ to SQL team的这些家伙太懒,不愿意写吧!)

如果用正常的比较方法,例如用 EndWith, StartWith, >,<,==, != ,以及他们的组合仍然无法满足要求(比如选择出所有符合e_mail pattern的string,就只能用证则表达式了),那就没有办法了,只能全部选择,然后在.net环境里面运用C#的灵活性进行二次选择了。如下:

DataContext dc = new DataContext("connection string");
Table<student> students = dc.GetTable<student>();

var result = students.AsEnumerable().Where(s=>Regex.IsMatch(s.name,"^k.*p$"));

在这里必须加上AsEnumerable(),通过这个AsEnumerable()就把 IQueryable<student>的对象变成了IEnumerable<student>,这个时候再进行后面的Where查询的时候就是玩的“本地查询”了,而不是“翻译查询”,当然,正则表达式也可以正常运行了

---------------

一些题外话:

到目前为止,微软官方发布的实现了IQueryable<TSource> 的产品只有 Linq to SQL 和Linq to Entity Framework。 对 Entity Framework的查询更加不一样,linq to sql是直接对sql server数据库查询,也就是说linq to sql 会把用户的查询翻译成sql server能够听得懂的语言-- T-SQL。

但是使用linq to entity framework查询datasource的时候是首先对一个中间层,叫Entity Data Model(EDM)进行查询,也就是说linq to entity framework会首先把用户的查询翻译成EDM能够听得懂的语言(好像叫SQL for EF),然后后面又有一套程序负责根据EDM的状态进行T-SQL查询。

民间有很多Ling to 的项目,比如 Linq to Google, Linq to Orcacle,等等,所有这些项目本质上就是实现这个IQueryable<TSource> 接口

因为IQueryable<TSource>继承自IEnumerable<T>,所以不关心后台运作的程序员不会感觉到这两者有什么区别,我也是Linq to SQL出了一次错才开始研究IQueryable<TSource>到底是什么玩意

rolia藏龙卧虎,小弟纯粹是闲得没事班门弄斧,如果以上哪里有理解不对的地方,还忘各位大佬指出,大家共同和谐进步更多精彩文章及讨论,请光临枫下论坛 rolia.net
Report

Replies, comments and Discussions:

  • 工作学习 / 学科技术讨论 / VS 2010 的一些新鲜小玩意
    本文发表在 rolia.net 枫下论坛试用了几个小时的VS2010,语言是C#和F#,.net 4.0新功能很多,我只挑了一些我感兴趣的写一点心得

    C# IDE中给我印象最深的就是“随心所欲的乱写”,然后IDE会帮助我完成后面的工作,学名叫 Code Generation。比如我新建一个Console Application,然后在main()函数里随便乱写一大堆我想象中的类和函数,如下:

    main()
    {
    student st = new student("Mr. Bush", 60);
    string name = st.GetName();
    int age = st.GetAge();
    }

    这个时候我并没有定义student类和类中的两个函数,这时IDE会画红线提醒我,只要右键点击红线IDE就会自动生成这个student类和它的2个函数string GetName()和int GetAge()

    非常方便

    --------------------------------------

    可选的类型参数
    比如我有一个函数有99个参数,
    public void f(string name, int age, string address, ....... int para98, int para99)

    调用这个函数,如果一个一个地写参数我还不如去出家当和尚,我记得在对Office编程的时候经常要用到有20个参数以上的函数,太痛苦了,VS2010大大减轻了这种痛苦,你可以在函数调用的时候有选择的assign value,如下:

    main()
    {
    f(name: "Mr. Bush",address: "USA", para98: 20);
    }

    这样就好了,太方便了,但是要对原函数定义作一点小小的修改,每一个可以被选择assign value的的变量要再定义的时候赋予一个default值,那么原函数修改后的定义如下
    public void f(string name = "", int age = 0, string address = "", ....... int para98 = 0, int para99 = 0)

    -----------------------------------------------------------------

    动态类型 Dynamic
    Dynamic 类型允许程序在运行时才开始实例化变量,而不是在编译时,这玩意表面上看起来很简单,也很容易上手,类似于动态语言,JavaScript 等等。但是我google了一下,发现越google内容越多,Dynamic的整个内容如果要完全理解至少要认认真真学一天时间吧!

    举个例子

    class student
    {
    public void showTime(string name)
    {
    Console.Write(name);
    }
    }

    main()
    {
    Dynamic s = new student();
    s.showTime("Mr. Bush");
    s.goSleep();
    }

    编译会正常通过,不会产生错误,但是在运行时会抛出异常,因为goSleep()没有在 student类中定义。以前我要完成类似的功能会写一大队的反射,很烦,现在Dynamic大大简化了这一步骤

    ---------------------------------------

    Tuple<>类型

    .net 4.0加入了一些新的类型,我觉得最实用的就属这个Tuple<>类型了,简直是爱不释手,举个例子说明Tuple<>是怎么回事

    以前我写函数如果希望返回n个不同类型的数值,我会用一个object[]来代替,比如:
    public object[] getNameAgeAddress()
    {
    return new object[] {"Mr.Bush",60,"USA"};
    }

    很明显的缺点就是没有类型定义,编译的时候不会出错,但很容易在运行的时候出错,因为要进行烦人的boxing,unboxing操作,效率很低。现在用Tuple<>改写一下:
    public Tuple<string,int,string> getNameAgeAddress()
    {
    return new Tuple<string,int,string>("Mr.Bush",60,"USA");
    }
    非常的清晰、方便,我以前在返回2个值得时候经常用KeyValuePair<T,S>,但是2个值以上就没有类似的类型了,只好用object[],现在可以用Tuple<>了

    ------------------------------------
    MEF框架 --- Managed Extensibility Framework

    这个东西很好玩,是.net 4.0新增加的一个框架,它最早是一个开源项目,在VS2008下也能用,是为了解决一个非常现实的问题而出现的 -- 为某个主程序很方便的写扩展程序,比如我写了一个俄罗斯方块游戏,但是我不懂美工,我希望其他人可以给我的砖块写美工程序,我只要使用他们写好的砖块就好了,这其实等于我写好一些砖块的标准接口,然后其他程序员继承这些砖块接口写程序,写完了我只要调用这些标准接口就好。这就是MEF框架要解决的问题,道理很简单。但实现起来并不容易。微软把这一步骤变得非常简单。事先导入System.ComponentModel.Composition,例子我就不写了,有些太长,大家有兴趣可以google一下

    ------------------------------------

    LINQ to object 没什么大的变化,增加了一个.Zip()扩展方法

    F#看了几个小时,有些晕,但是觉得F#非常好玩,C#写10行的代码,F# 2-3行就完了。F#以后应该会有巨大潜力,C# 3.0语法的很多灵感来自于函数编程。F#的主设计师说F#以后或许会成为计算的核心语言,C#和VB.NET会利用F#的结果构建外围的UI。

    -----------------------------------

    Windows Workflow 4
    不熟悉WF,但是看资料说workflow 4.0比3.0改变了太多太多,是一次非常“激进”的升级。一个有经验的workflow 3程序员也要花很多时间和精力重新学习workflow 4新增加的很多概念和架构。但是所带来的好处是巨大的,资料上说4.0比3.0运行速度快了10-100倍更多精彩文章及讨论,请光临枫下论坛 rolia.net
    • Thanks. 也想download试试。不知WPF改进了多少,速度是不是提高了。
    • +1. Any review for C++ supportfeatures?
    • Very good introduction
    • 微软的特点就是开发快,成本低。看来程序员的标准又降低了。
      • depends on how you look at this
    • 学习笔记:LINQ 查询的一些区别
      本文发表在 rolia.net 枫下论坛我贴的这些都是我平时的学习笔记,过一段时间这些知识会忘记,我希望在我还清醒的时候把这些玩意以尽量通俗易懂的方式记录下来,以备不测:

      Linq 有两种查询,完全按照资料翻译就是“本地查询”Local Query 和“翻译查询”Interpreted Query,听着很令人困惑,没办法,英文原文如此。这两种查询很多程序员往往不加区别的使用,在大多数情况下没有任何问题,两种查询中所暴露出来的使用方法是一模一样的。就像我开QQ车和开Toyota车,暴露给驾驶员的开车方法没有任何不同,都是油门刹车,但是QQ直接把刹车力度的大小传递到刹车鼓上,而Toyota会把刹车力度大小首先送给内部计算机,计算机“翻译”一下,再决定是应该自动爆冲还是立即刹车。Linq的2种查询有些像我说的这个例子

      半年多前我在使用Linq查询的时候遇到了一个异常错误,在解决问题的时候理解了这两种查询的不同之处,我出错的程序是这样的:

      我有一个对数据库表操作的(Linq to SQL)查询,表名是student, fields 分别是
      create table student
      (
      name varchar(20),
      age int,
      address varchar(20)
      )

      在C#中,这个表的定义类是这样写的:

      [Table]
      public student
      {
      [Column]
      public string name;
      [Column]
      public int age;
      [Column]
      public string address;

      public student(string n) { name = n; }
      }

      我要找出所有 name 符合某种Regular Express的student,理想的写法应该是这样的
      main()
      {
      DataContext dc = new DataContext("connection string");
      Table<student> students = dc.GetTable<student>();
      // 找出所有name以k开头,以p结尾的student
      var result = students.Where(s=>Regex.IsMatch(s.name,"^k.*p$"));
      foreach(student s in result) Console.WriteLine(s);
      }

      程序运行时出错,提示T-SQL没有相应的函数,我把程序改成这个样子
      // 找出所有name以k开头,以p结尾的student
      var result = students.Where(s=>s.name.StartWith("k") && s.name.EndWith("p"));

      程序运行正常,呵呵,区别原来在这里
      student.Where(....)返回的是这样一个东西

      IQueryable<student> result = students.Where(s=>Regex.IsMatch(s.name,"^k.*p$"));

      一旦返回值是IQueryable<T>,那么这样的查询就会被称为是“翻译查询”。“本地查询”所返回的值永远是IEnumerable<T>,比如这个例子
      var result = (new student[] {new student("Bush"),new student("Tom"),new student("kacp")}).Where(s=>Regex.IsMatch(s.name,"^k.*p$"));

      同样的类型,同样的student,只不过我在本地把它封装成一个数组,在用Where查询的时候,它的返回值就是一个IEnumerable<student>的值,完整的写出是这个样子:
      IEnumerable<student> result = (new student[] {new student("Bush"),new student("Tom"),new student("kacp")}).Where(s=>Regex.IsMatch(s.name,"^k.*p$"));
      这个查询就是所谓的“本地查询”,而且这个本地查询不会出错,正则表达试运行得非常好

      看看区别,首先从语法定义上看:
      本地查询:public static IEnumerable<TSource> Where<TSource> (this
      IEnumerable<TSource> source, Func<TSource,bool> predicate)

      翻译查询:public static IQueryable<TSource> Where<TSource> (this
      IQueryable<TSource> source, Expression<Func<TSource,bool>> predicate)

      最大的区别在于
      本地查询:Func<TSource,bool> predicate 和
      翻译查询:Expression<Func<TSource,bool>> predicate

      本地查询的表达式会在编译的时候直接被编译器编译成委托变成.dll或者.exe加以运行,但是翻译查询的表达式首先会变成一个“表达式树”(Expression<T>)静静的躺在程序里面,直到运行时这个“表达式树”才会被“翻译”成对象能够理解的“查询”(比如T-SQL,或者在Entity Framework里是一种特殊的对Entity Data Model进行查询的SQL)加以运行,是不折不扣地“动态”运行,这也就是为什么我的Linq to SQL 的 Where查询在编译的时候不会出错,因为编译器不理会你要做什么,只是帮助你检查你的C#“查询”语法有没有错误,而这个查询到底能不能用直到运行时才会知道。

      那么是谁负责“翻译”我的“表达式树 查询”呢?在我的例子中,是DataContext类,它里面有一套程序专门负责在运行时把我传递给它的“表达式树 查询”[在这个例子中是:.Where(s=>Regex.IsMatch(s.name,"^k.*p$")]翻译成数据库能够听得懂的语言,当然就是T-SQL了,然后运行这个T-SQL,在把返回结果变成IQueryable<T>的形式返回给.net。

      现在我的这个错误就很好理解了,因为T-SQL中没有Regular Express的操作,所以DataContext无法把我的查询请求翻译成T-SQL语言,于是就会报错。而我的另外一种查询
      students.Where(s=>s.name.StartWith("k") && s.name.EndWith("p"));
      DataContext里面有相应的功能把它们翻译成T-SQL运行,所以就运行正常啦(我想理论上微软如果愿意肯定也能支持正则表达式,可能LINQ to SQL team的这些家伙太懒,不愿意写吧!)

      如果用正常的比较方法,例如用 EndWith, StartWith, >,<,==, != ,以及他们的组合仍然无法满足要求(比如选择出所有符合e_mail pattern的string,就只能用证则表达式了),那就没有办法了,只能全部选择,然后在.net环境里面运用C#的灵活性进行二次选择了。如下:

      DataContext dc = new DataContext("connection string");
      Table<student> students = dc.GetTable<student>();

      var result = students.AsEnumerable().Where(s=>Regex.IsMatch(s.name,"^k.*p$"));

      在这里必须加上AsEnumerable(),通过这个AsEnumerable()就把 IQueryable<student>的对象变成了IEnumerable<student>,这个时候再进行后面的Where查询的时候就是玩的“本地查询”了,而不是“翻译查询”,当然,正则表达式也可以正常运行了

      ---------------

      一些题外话:

      到目前为止,微软官方发布的实现了IQueryable<TSource> 的产品只有 Linq to SQL 和Linq to Entity Framework。 对 Entity Framework的查询更加不一样,linq to sql是直接对sql server数据库查询,也就是说linq to sql 会把用户的查询翻译成sql server能够听得懂的语言-- T-SQL。

      但是使用linq to entity framework查询datasource的时候是首先对一个中间层,叫Entity Data Model(EDM)进行查询,也就是说linq to entity framework会首先把用户的查询翻译成EDM能够听得懂的语言(好像叫SQL for EF),然后后面又有一套程序负责根据EDM的状态进行T-SQL查询。

      民间有很多Ling to 的项目,比如 Linq to Google, Linq to Orcacle,等等,所有这些项目本质上就是实现这个IQueryable<TSource> 接口

      因为IQueryable<TSource>继承自IEnumerable<T>,所以不关心后台运作的程序员不会感觉到这两者有什么区别,我也是Linq to SQL出了一次错才开始研究IQueryable<TSource>到底是什么玩意

      rolia藏龙卧虎,小弟纯粹是闲得没事班门弄斧,如果以上哪里有理解不对的地方,还忘各位大佬指出,大家共同和谐进步更多精彩文章及讨论,请光临枫下论坛 rolia.net
      • LINQ is somehow limited in querying. If you use Entity SQL, your query can be easily expressed as
        using (YourEntity entity = new YourEntity())
        {
        ObjectQuery<students> result = entity.students.Where("it.name LIKE ’k%p’");
        }
    • 俺也不太知道WF, LINQ微软早就说要放弃, 减少功能是正常的. VISUALSTUDIO则全是没用的花架子, 足以让程序更乱, 象是弄进去了一个叫CODE RUSH的破东西.
      • 许多东西自己做比学还快,都不知道咋整?有点无所适从。
    • Better Coding with Visual Studio 2010
    • What’s New in Visual Basic 2010. Embed Interop Types,这个有用。Multi-Targeting,这个VS2008里已有。别的一简化,打工仔更加没事做。
    • 化了一天装好了,每次微软出新版本就另搞一套新色调,觉得VS2010色调特难看,好多地方背景色前景色太接近,看着太费眼神,吃饱了撑的,有没有选项让它用VS2008/2005的风格?
      • 气死我了,刚装好试用版,作天在MSDN 上一看,正式版可以下载了。