兴国安邦

C# 3.0, Linq, Linq To Sql

博客园 首页 新随笔 联系 订阅 管理
  33 Posts :: 0 Stories :: 530 Comments :: 52 Trackbacks
先给关注dlinq的朋友们道歉,最近工作实在忙,没有时间来写blog。从本节开始,我们讲dlinq语法咯。我们先从select子句讲起。看下面的例子。
    var q =
        from c 
in db.Customers
        select c.ContactName;

这是一个最简单的dlinq查询语句,查询得到联系人的名字。在这里,我需要提醒下大家的是,像这个语句只是个声明,dlinq并没有真正把数据取出来,只有当你需要该数据的时候,它才会帮你去取,这就是延迟加载(deferred loading)。如果,你想在声明的时候就希望dlinq帮你取到数据,你可以使用ToList() 或ToArray()方法。如上例。

        var q = (from c in db.Customers
                 select c.ContactName).ToArray();
        var q = (from c in db.Customers
                 select c.ContactName).ToList();

在这里,我还要提醒大家一点。dlinq返回的结果集是对象的集合,不是数据的。
 在dlinq执行的时候,它会先将上面的标准查询转换成dlinq的API(也有人叫级连方法),比如,下面语句

            var q =
                from c 
in db.Customers
                where c.City 
== "London"
                select c;

就会先被转化成 var q = db.Customers.Where(c=>c.City== "London").Select(c=>c); 也就是说,这两个语句是等价的。而后,dlinq会解析影射文件,根据dlinq的query语句,自动产生sql语句,并把sql送到sql server服务器,根据返回的数据集,创建相应的对象。在这里,你可能会对c=>c感到非常陌生。这是Lambda表达式(expression),你可以理解c为结果集里的任一对象,这对象的类型是和你结果集里元素类型是一致的 。这里理解起来可能困难。我们一起来理解下数据即是对象的概念。我相信这会帮我们理解Lambda表达式。

在dlinq之前,在java领域有Hibernate,在net领域有NHibernate技术,来实现object/relational 持久和查询服务。dlinq其实质上,是在吸收了众多技术的基础上,比他们更加强大的工具。数据即对象的含义有两层。第一,数据结构(表结构)即是类。可以描述为Table Schema--Class。第二,表里的数据即是变量,描述为Data--object(variable)。那么,我们在来理解Lambda表达式可能就容易些。刚才我们已经说了,var q = db.Customers.Where(c=>c.City== "London").Select(c=>c);将会返回Customers对象的集合,也就说,这个集合的每个元素就是一个Customer。Lambda表达式是对c# 2.0中的anonymous methods(匿名方法)的扩展。它更加简化匿名方法的实现形式。这里的c是一种隐式的声明,编译器会自动推断它的实际类型,也可以显示声明,比如,  var q = db.Customers.Where((Customer c) => c.City == "London").ToList();  Lambda表达式用=>符号跟随一个表达式,这个表达式,需要返回一个类型,其实质就是一个方法返回一个类型。它只是更加简洁的匿名方法。然后,where等操作符用它返回的这个类型做为参数。关于Lambda表达式的具体实现,我会在进阶部分详细讲解。这里不再赘述。

有一点要提醒大家的是,标准的查询语句,必须是select语句在最后,而级连表达式,各种操作符的位置并不是很重要。比如var q = db.Customers.Where(c=>c.City== "London").Select(c=>c); 可以写成var q = db.Customers.Select(c=>c).Where(c=>c.City== "London");它们两个是一样的,但是,标准查询就不可以换位子,select语句必须在最后。虽然在级连表达式,各种操作符的位置并不是很重要,但是他们还是有区别的。特别是在使用匿名类后,区别很明显。但万变不离其宗,我们只要记住,下一个操作符总是在上一个操作符所筛选的数据集的基础上进行筛选。这点,我会在以后的blog中,更加详细的说明。
在select语句中,另一个难点是匿名类。比如列子

            var q =
                from c 
in db.Customers
                select 
new {c.ContactName, c.Phone};

其实不光在select操作中有匿名类,其他操作中也有。让我们一起来理解下匿名类。上面的语句与
var q = db.Customers.Select(c=>new {c,ContactName,c.Phone});是等价的。匿名类是c# 3.0中新出现的特性。其实质是编译器根据用户定义,自动产生一个匿名的类,帮用户实现临时变量的储存。注意,是临时变量。大量使用匿名类会使程序可读性降低。匿名类还依赖于另外一个特性,就是在c# 3.0可以支持根据property来创建对象。比如,有类

    public class Person
    {
        
private string name;

        
public string Name
        {
            
get { return name; }
            
set { name = value; }
        }
    }

以前,我们只可以用构造函数来创建其对象,现在在3.0中支持用property来创建,即,可以用
 var d = new Person { Name = "s" }; 来创建对象。在这里,你可能还对var类型产生疑问。你可能以为c#3.0和javascript一样是弱类型的。其实var并不是c#3.0的类型,它是编译器的关键字,编译器根据实际变量的返回类型,自动推断类型。那么var c = null; 是无法编译通过,因为编译不知道null代表那个类型。所以,c#3.0还是强类型的。

现在3.0可以支持用property来创建对象了,那么就有了匿名类的出现。比如,var d = new { Name = "s" };编译器自动产生一个有property叫做Name的匿名类,然后按这个类型分配内存,并初始化对象。在这个地方,还有个问题,比如,var d = new {  "s" };是编译不通过的。因为,编译器不知道匿名类中的property的名字。但是,如果,        string c = "d";     var d = new { c}; 则是可以通过编译的。编译器会创建一个叫做匿名类带有叫c的property。

在dlinq中,比如new {c,ContactName,c.Phone});这里出现ContactName和Phone都是我们在影射文件中定义的和表中字段相对应的property。编译器在取会数据并创建对象时,会创建一个匿名类,这个类有两个属性,为ContactName和Phone,然后根据数据初始化对象。匿名类还有另外一种形式。

            var q =
                from e 
in db.Employees
                select 
new {Name = e.FirstName + " " + e.LastName, Phone = e.HomePhone};
这种形式和第一种不同的是,编译器会重命名property的名字。当然也可以把两种形式组合起来。
            var q =
                from p 
in db.Products
                select 
new {p.ProductID, HalfPrice = p.UnitPrice / 2};

第一个属性的名字不会变,第二个会被重新命名。
好,就先讲这几个,下节我会介绍几个更复杂的用法。
posted on 2006-12-20 22:06 Tom Song 阅读(8280) 评论(28)  编辑 收藏 所属分类: C# 3.0

Feedback

#1楼  2006-12-20 22:12 neuhawk      
一直觉得linq不错,期待ado.net v next
  回复  引用  查看    

#2楼  2006-12-20 23:02 TerryLee      
@neuhawk
感觉DLinq更像Castle中的AR,而ADO.NET v next 则更像NHibernate:)
  回复  引用  查看    

#3楼  2006-12-20 23:05 木野狐      
啥时候会正式 release 有消息么?
  回复  引用  查看    

#4楼  2006-12-21 09:21 benfish      
查询var q = db.Customers.Where(c=>c.City== "London").Select(c=>c); 和查询var q = db.Customers.Select(c=>c).Where(c=>c.City== "London");的执行效果是一样的,但是它们的执行效率就不同了,第二个基本上是把所有的记录从数据库读出来,然后在客户端进行过滤。
不知道我这么理解对不对?
  回复  引用  查看    

#5楼  2006-12-21 09:31 Boler Guo      
好,期待更多大作~~
  回复  引用  查看    

#6楼 [楼主] 2006-12-21 11:11 宋国安      
回复你的问题,我已经更新本文章的该部分,但是他们的执行效率是一样的。因为dlinq是采用延迟加载技术,当你真正使用数据的时候,它才给你加载。针对你的问题,无论第一个还是第二个,都是先翻译成sql语句,送到sql server服务器,返回结果集,并创建对象。而你说的在客户端进行过滤,是linq的一个功能。linq和dlinq的区别就在这里。linq是对内存进行操作,而dlinq的操作对象为数据库。如果,你把数据先读出来,放在内存中,然后再过滤,那就是linq的功能了。要想实现你的想法,必须这么来写,var q = db.Customers.Select(c=>c).ToList().Where(c=>c.City== "London"); 注意,我加了ToList()的方法。这样,dlinq在它碰到第一个ToList()时,就已经先发送请求到数据库,然后,返回结果集。而where操作符这时就是在客户端进行过滤了。如果,你想察看dlinq对数据发送了什么样的请求,在你new出 db对象时,加一句
db.Log = Console.Out;它就会将所有对数据库的操作从屏幕输出。这时,你就可以看到var q = db.Customers.Select(c=>c).ToList().Where(c=>c.City== "London"); 与 var q = db.Customers.Select(c=>c).Where(c=>c.City== "London"); 是不同的啦

@benfish

  回复  引用  查看    

#7楼 [楼主] 2006-12-21 11:13 宋国安      
应该会和vs 2007 一起发布。相信不久的将来,你就可以看到了。@木野狐

  回复  引用  查看    

LZ,有两个问题:
1."就会先被转化成 var q = db.Customers.Where(c=>c.City== "London").Select(c=>c); 也就是说,这两个语句是等价的。而后,dlinq会解析影射文件"
-------
你这里说的映射文件指的是?
2.个人觉得你解释Lambda的地方有点牵强"......所以就有了Lambda表达式"...我看得有点昏.呵.我觉得可以单独开篇,从委托,匿名委托,最后到Lambda,这样来得比较直接...个人建议:)
  回复  引用    

BTW:
@TerryLee:
你怎么知道我想些什么呢?:)
  回复  引用    

#10楼 [楼主] 2006-12-22 11:50 宋国安      
关于第一个问题,请参考 入门三http://www.cnblogs.com/126/archive/2006/09/06/492332.html 一文。
关于第二个问题,我稍候会更改本文,我也感觉这么写有点问题。你说的是Lambda表达式的具体实现。我在寻求一种更直观的理解方式,希望能帮大家快速理解Lambda表达式。我会翻译一篇关于Lambda表达式的文章,以飨读者。
有什么不足的地方,还请多多指正。
@Woody[匿名]

  回复  引用  查看    

哦,你说的"影射文件"指的是.cs文件,我一看到"映射"就想到ado.net orcas的xml映射文件了.呵.我是说Dlinq是通过特性来完成映射的啊:)
  回复  引用    

#12楼 [楼主] 2006-12-22 17:59 宋国安      
其实,dlinq不光可以通过attribute来完成映射,也可以把这些attribute存储在xml里,实现和cs的分家。比如,本文第二章中,提到的101 sample,有一个例子,叫做external mapping,就是把数据库的相关信息存储在xml文件里。sqlmetal也支持产生xml mapping.它里面就有个选项,
/map[:file] Generate XML mapping file instead of attributes

你加上这个选项后,就产生xml的mapping,cs里就没有那些attribute了。
@Woody[匿名]

  回复  引用  查看    

#13楼 [楼主] 2006-12-29 20:35 宋国安      
刚更新了lambda expression 部分。
  回复  引用  查看    

唉,看见var我就想起了JavaScript,然后****连前天吃的东西都吐出来了!
  回复  引用    

#15楼 [楼主] 2007-01-29 09:41 宋国安      
c#3.0中的var和JavaScript有着本质的区别,首先,var并不是c#3.0语言中的关键字,它只是编译器的,也就是说clr中,压根就没有这种类型。var所代表的实际类型,是编译器根据赋给的值推导出来的。
@JavaScript去死吧!

  回复  引用  查看    

#16楼  2007-01-29 13:34 装配脑袋      
Lambda表达式在处理DLinq的时候并不是anonymous methods的一种表现形式,而是会转化为Expression Tree,这个Tree会纪录下Lambda表达式解析之后的语法树。DLinq会根据语法树生成正确的查询。因此表面上是客户端检索,其实还是可以远程检索的。
  回复  引用  查看    

#17楼  2007-07-01 00:39 icezs [未注册用户]
var q = db.Customers.Select(c => c).ToList().Where(c => c.City == "London");
//var q = db.Customers.Select(c => c).Where(c => c.City == "London");

dataGridView1.DataSource = q;
第一行查询dataGridView1里没数据,第二句被注释的有记录显示在dataGridView1里。是啥原因啊
  回复  引用    

#18楼 [楼主] 2007-07-06 14:41 宋国安      
加.ToList()

var q = db.Customers.Select(c => c).ToList().Where(c => c.City == "London").ToList();
@icezs

  回复  引用  查看    

#19楼  2007-07-11 17:49 txd [未注册用户]
为Linq而生的Lambda表达式
  回复  引用    

#20楼  2007-07-26 15:10 Sean [未注册用户]
每一篇的结尾能不能加一个下一节的链接,这样连起来看比较方便
  回复  引用    

#21楼  2007-11-07 11:01 lackboy [未注册用户]
关于延迟加载得疑问。
1 DataClasses1DataContext db = new DataClasses1DataContext();
2 var pro = from p in db.Products
where p.CategoryID == 2
select p;
1处代码执行晚后,db.Products中就已经有数据了。貌似2处的代码是在内存进行数据筛选。
我使用的是VS2008 express.
  回复  引用    

#22楼 [楼主] 2007-11-28 20:11 Tom Song      
@lackboy
这个问题,vs没有加载.但是,在你试图用跟踪器看里面的数据的时候,这个时候,vs开始动作,加载了数据。

你可以尝试把 db.Log = Console.Out;
盯住屏幕输出,没有看数据前,是没有输出的,一但尝试看数据,利马就有sql语句出来了。
  回复  引用  查看    

#23楼  2007-12-23 16:20 abing293 [未注册用户]
有个问题请教楼主:
this.DataList1.DataSource = (from n in db.News
select new { n.Id, n.NewTitle }).Skip
(0).Take(20);
this.DataList1.DataBind();
然后我要在DataList的ItemDataBound事件中修改显示格式,问题是如何获得e.Item.DataItem中的数据啊。
protected void DataList1_ItemDataBound(object sender, DataListItemEventArgs e)
{
e.Item.DataItem;//是Object类型的,如何转换为News(是实体类)类型啊。
}
  回复  引用    

#24楼  2008-05-29 12:58 miky [未注册用户]
答23楼的,按照我使用NHibernate,你应该建立一个对象,例如AObject,然后通过这样来做:
IList<AObject> aobjects =
(from n in db.News
select new{n.Id,n.NewTitle}).Skip(0).Take(20);
this.DataList1.DataSource = aobjects;
this.DataList1.DataBind();
protected void DataList1_ItemDataBound(object sender, DataListItemEventArgs e)
{
(AObject)e.Item.DataItem;//是Object类型的,如何转换为News(是实体类)类型啊。
}

其中AObject对象包含两个属性:Id、NewTitle。
以上不知是否正解。
  回复  引用    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-07-11 09:18 编辑过


相关链接: