朱宇注:
这个WebCast我个人觉得很好,通过演示可以直观的看出来过渡到C#3.0和使用Linq的好处,希望大家也能受益.

原Web Cast下载地址:.NET Language Integrated Query Framework

因为一个多小时的WebCast内容比较多,所以我将这次的WebCast分为两部分.
由于没有联系到Luca Bolognese本人,所以代码是我自己写的.文章中使用注释帮助大家找到每节内容的对应代码.

代码下载地址:Luca Bolognese at Tech Ed 2007 Demo

下面我们来看第一部分.

1)为什么要使用LINQ?
我们并不认为Linq解决了什么大的问题,它实际上是针对目前程序开发中数据和对象之间'阻力抗衡'问题的一个解决方案.现在的程序员有了很好的开发环境,写好代码后可以设置断点,进行调试;如果哪段代码出错了,编译器会弹出提示,给出错误信息,还可以通过智能感知查看代码的上下文环境等等.这些就如同你开着法拉利兜风一样轻松自如,令人愉快.不过当要访问数据的时候问题就来了,这时候的感觉会糟透了!让我们就拿数据库举例吧,它就像一个暗箱,唯一能和它交互的方式就是字符串.我们传给它一个字符串,这家伙会回复给我们一个矩阵结构的数据,并且这些数据完全是弱类型的,编译器不会懂得这些数据的含义,我们不能对这些数据进行调试,当然也没有智能感知的帮助了,这就像回到了十年前.而实际上当我们对XML进行操作的时候也不比对数据库操作轻松多少!如果还拿刚才的法拉利比喻的话,当遇到了数据操作我们就像突然撞车一样.所以,这时候我们常说的'用户体验'就简直是糟透了.
尽管如此糟糕,数据操作对于程序开发来说却是一个非常重要的部分.就以查询为例,我们通常的做法是使用一个集合的概念来包装我们所有的成员;在编程语言中我们经常都是创建一个List,向List中添加每一个成员,当需要查询数据时,提交查询语句便可以得到结果,这样一路走来非常顺利,简直就是轻松自如.而LINQ项目就是专门处理刚刚我们提到的这么一系列常规做法.所以有了Linq,我们既可以像平时操作对象一样轻松的操作数据了,与此同时,象查询一类的数据库字符串,也被集成到编程语言中了.这就是LINQ项目的目标.

 

2)如何使用LINQ?

做为开始认识Linq我们先仅介绍查询这部分概念.当在.NET 3.5平台下进行查询时,可以不在象以前那样使用T-SQL语句字符串了,我们可以使用.NET平台下的编程语言进行查询.那么3.5平台下的编程语言都可以查询什么类型的数据呢?当然LINQ提供了对很多类型数据的操作: I)a)Objects(对象),如果你将内存中的对象看作是数据,那么这些对象自身有字段,对象之间有关系,而将它们组合到一起就像存在内存中的一个小数据库.正是得益于这种对集合的查询的概念我们才有了目前的数据对象映射概念,如果仔细想想,我们大家一直都在使用这种方法.试想一下每次使用foreach遍历集合中的每个元素时,我们都是在查询,只是使用了命令性强制性的实现,我们明确地告诉编译器要做什么. 第二种类型是 II)关系型数据库,在启用了LINQ的3.5平台上的ADO.NET中,既可以使用Linq查询内存中的 b)DataSets(数据集),也可以使用Linq查询SQL数据库.我们为操作关系型数据库提供了两种框架:一种是 c)LINQ to SQL(DLinq框架),它为访问关系型数据库提供了一种简单的,直接的,方言式的数据库访问,如果你需要一种简单的数据和实体关系映射框架,LINQ to SQL是最合适的最快速获取数据的框架;另一种关系型数据库操作框架是 d)LINQ to Entities(实体框架),它提供了更复杂的更丰富多样的数据实体映射;假设你的数据模型和你的数据库结构差距比较大,LINQ to Entities便是最好的解决办法.第三种数据阻抗来自 III)e)XML.以上就是LINQ项目要面对的三大对象:对象,关系型数据库,XML.

 

3)DEMO : The LINQ Project
(注:参考工程项目代码Part I C# 2.0 demo)
那么剩下的时间将做一个比较大的演示.
首先我会按照大家的习惯写出C#2.0的应用程序,然后我会把这个C#2.0的程序使用C#3.0的新特性改写并接着一点一点地演示我刚刚为大家介绍的内容,那么我们开始吧.
我们做演示的应用程序是用来操作客户信息的,所以我们先来写一下客户(Customer)这个类.

class Customer
{
    string _CustomerID;
    string _ContactName;
    string _City;
    public string CustomerID
    {
        get { return _CustomerID; }
        set { _CustomerID = value; }
    }
    public string ContactName
    {
        get { return _ContactName; }
        set { _ContactName = value; }
    }
    public string City
    {
        get { return _City; }
        set { _City = value; }
    }
}

那么接下来写一下Driver类,该类包含主方法.

class Driver
{
    static void Main(string[] args)
    {  }
}

我们需要一个方法来读取我们的Customer类的客户数据,我们就把这个方法写在Driver类中吧.

class Driver
{
    static void Main(string[] args)
    {        }
    static IEnumerable<Customer> LoadCustomers()
    {
        List<Customer> customers = new List<Customer>();
        Customer c = new Customer();
        c.CustomerID = "ALFKI";
        c.ContactName = "Maria Anders";
        c.City = "Berlin";
        customers.Add(c);
        #region More Customers
        . . .
        #endregion
        c = new Customer();
        c.CustomerID = "WOLZA";
        c.ContactName = "Zbyszek Piestrzeniewicz";
        c.City = "Warszawa";
        customers.Add(c);
        return customers;
    }
}

这里我们需要一个Customer对象的集合,所以我在方法中首先创建一个客户对象的List<Customer> customers,然后创建了一个Customer c,对c的每个属性赋值,然后将c添加到List中,最后返回customers.那么现在通过LoadCustomers方法便可以得到一个Customer的数据集合.
那么这个应用程序要实现的是找出所在城市名以M开头的城市以及这些城市中的客户总数.假设现在我们在做一个网站,而现在要完成的功能将被作为网站的一个网页.那么现在我需要一个类来代表我所查询出来的结果,所以我们来写一个Result类.

class Result
{
    private string _City;
    private int _Count;
    public string City
    {
        get { return _City; }
        set { _City = value; }
    }
    public int Count
    {
        get { return _Count; }
        set { _Count = value; }
    }
} 

在Result类中,我们需要两个字段,一个string类型的_City来存放城市名,一个int类型的_Count来存放该城市中客户的数量.接下来我们在主方法中写查询操作的实现代码.

static void Main(string[] args)
{
    List<Result> results = new List<Result>();
    foreach (Customer c in LoadCustomers())
    {
        if (c.City.StartsWith("M"))
        {
            Result res = results.Find(
                delegate(Result r) { return c.City == r.City; }
            );
            if (res == null)
            {
                res = new Result();
                res.City = c.City;
                res.Count = 1;
                results.Add(res);
            }
            else
            { res.Count++; };
        }
    }
    foreach (Result r in results)
    {
        Console.WriteLine("City:{0}\tCount:{1}", r.City, r.Count);
    }
}

首先我们需要一个查询结果的集合, List<Result> results,然后foreach遍历LoadCustomers方法返回的Cusomer集合,如果客户的所在城市是以M开头,看一下查询结果集中有没有和当前客户所在的城市相同的结果类(Result)对象,如果没有说明这是第一次发现该城市,创建一个新的结果类对象,把当前客户的城市赋给该对象的City属性,当然客户数就是1了,最后把该对象加到List<Result>中;如果已经有了,就直接在客户数上加1就可以了.
为了方便显示结果,我写了一个简单的小对象ObjectDumper,来代替麻烦的foreach输出.

ObjectDumper.Write(results);

OK,到现在为止我们用C#2.0完成了这个应用程序,当然大家可以用Dictionary来写出更漂亮的程序来,但是无论怎么样.这些代码都是C#2.0中为了实现功能大家避免不了,一定要写的.那么接下来我们一起来看一下C#3.0为我们带来了什么!
(注:参考工程项目代码Part II C# 3.0 demo)
1).我们导入一下命名空间.这些名称空间并不是要一次全部导入,只是下面还有关于LINQ Framework各个子框架的演示,所以先把所有演示所需要的命名空间都导入进来.

using System.Linq;
using System.Data.Linq;
using System.Data.SqlClient;
using System.Data.Linq.Mapping;
using System.Xml.Linq;

2).大家留意一下Customer类,这个类里面有字段,然后我们通过公共属性的getter和setter将字段放出来供程序使用.不过这些字段只用来存储一个值,和程序的业务逻辑并没有任何关系.所以在新版本的C#3.0中.我们可以只设定公共属性,而编译器会自动为我们生成后台的私有字段.所以我们可以将Cusomer类改写成为:

class Customer
{
    public string CustomerID { get; set; }
    public string ContactName { get; set; }
    public string City { get; set; }
}

3).我们再来看一下这行代码List<Result> results = new List<Result>();不知道大家有没有觉得这里有些代码是多余的?也许有人注意到了,我们写了两次List<Result>,可能大家都希望能把它写得再简单些,那么我们我们可以写成这样: var results = new List<Result>();我们不再需要告诉编译器两次results的类型的List<Result>,放一个var在前面占位,编译器会自动将var替换成List<Result>,当我的鼠标移动到var上面,大家可以看到,编译器会提示出这个results对象的类型是List<T>,并且T是Result这个类.同样的,我们也可以将foreach (Customer c in LoadCustomers())中的Customer改写成var,这样当鼠标移动到var上我们可以发现编译器仍然知道c的类型是Customer.
4).OK,让我们来看看这一段:delegate(Result r) { return c.City == r.City; },这是一段匿名委托代码,匿名委托的作用是创建一段函数体来代替一个方法.不过匿名方法的写法和理解上还是非常不方便的,所以我们现在有了新的语法,我们可以这样写:

Result res = results.Find( customer => customer.City == c.City );

我们称这种语法为lambda表达式,这是书写委托更好的方式,更易于书写,更易于阅读.
5).接着往下看这一段:

res = new Result();
res.City = c.City;
res.Count = 1;
results.Add(res);

创建一个Result对象为每一个属性赋值,然后添加到集合中.C#3.0为我们提供了一种更易于书写创建对象的语法,我们可以通过这样一段代码重写上面的这一段:

results.Add(new Result() { City = c.City, Count = 1 });

这行代码也是创建了一个Result对象然后直接为这个对象的各个属性赋值,这样便可以方便的只用一行代码同时创建并初始化一个对象.可能有人会问,既然我们可以这样创建初始化一个对象,那么集合可以这样操作么?当然可以,我们可以以同样的办法创建并初始化集合.再来回头看看程序的LoadCustomers方法,我们完全可以改写成下面这段代码:

static IEnumerable<Customer> LoadCustomers()
{
    var customers = new List<Customer>
    {
        new Customer{ CustomerID = "ALFKI",
                               ContactName = "Maria Anders",
                               City = "Berlin" },
        #region More Customers
        . . .
        #endregion
        new Customer{ CustomerID = "WOLZA",
                               ContactName = "Zbyszek Piestrzeniewicz",
                               City = "Warszawa" }
    };
    return customers;
}

这里使用了新语法集合初始化器,先是创建了一个List<Customer>,然后立即初始化这个集合中的各个Customer对象.这里大家可以看到我们初始化一个集合时也不再像以前一样需要不断的创建对象添加对象,只需要简简单单的将集合中的每个元素跟在集合后面就可以了,非常方便.

到此呢,我们把原来的代码以一种更简单的新语法改写了一下.不过如果有人认真观察,认真思考了的话,可能会有上当的感觉,尽管我们介绍了C#3.0的一些新语法,可是到此刻我们并没有改变过这个应用程序实质性代码,只不过是简化简化这,简化简化那而已.是的,事实上到现在才真正开进入正题.大家来看一下这段代码:

var results = new List();
foreach (var c in LoadCustomers())
{
    if (c.City.StartsWith("M"))
    {
        Result res = results.Find(customer => customer.City == c.City);
        if (res == null)
        { results.Add(new Result() { City = c.City, Count = 1 }); }
        else
        { res.Count++; }
    }
}

这段代码没什么错,只是它有一点点太强制性了,并且它太过’低级’.它告诉编译器要遍历每一个集合中的元素,并指明了找到符合条件的元素后怎么样处理.这不是仅仅是程序的梗概,而是写明了程序每一个细节,具体到不能再具体.我想大家都希望有一种更好的方法来完成这段代码,我认为LINQ为大家提供了这个更好的方法.使用Linq我们来重写一下上面功能的代码.

var results = from c in LoadCustomers()
                       where c.City.StartsWith("M")
                       group c by c.City into g
                       select new Result { City = g.Key, Count = g.Count() };

从LoadCustomers返回的集合中遍历每一个Customer类的对象c,找出c的城市名以M开头的,再将满足条件的c根据c的城市名分组放在g里面,最后我们挑选出Result结果集,结果集中的城市名是分组的关键字也就是c的城市名,而数量便是g中每个分组里面的元素个数,就是这样.
如果大家联想一下,这段代码很可能会让大家想起SQL,尽管有些不一样的地方,不过我想这段代码很好理解.但是现在我想带着大家从另一个不同的角度再来看一遍这段代码,大家仔细看看这段查询代码的各个子句.我们一个子句一个子句的说,from子句为我们给出了一个对象的集合;from子句后面是where子句,where子句在from子句给出的集合中进行筛选,选出符合条件的一组对象,实际上where子句最终得到的是from子句集合的一个子集;where子句后面是group子句,group子句得到where子句的集合后将他们按照条件进行分组,将同一类型的对象归到一组里,最后返回一个装有多个组的集合;最后是select子句获得group子句分过组的集合,然后根据这组数据来创建Result结果集.如果大家对这部分细节或者语言背后的详细实现感兴趣,想深入了解,那么大家可以参考另一个演示’Microsoft Visual C# Under the Covers:An In-Depth Look at C# 3.0’.

我们回到正题,接下来需要大家注意的一点是LoadCustomers这个方法的返回值.LoadCustomers这个方法的返回值类型是IEnumerable<T>.正因为LoadCustomers方法的返回值类型是IEnumerable<T>,所以我们才可以使用Linq查询这些数据.大家注意一下,在.NET类库中,所有的集合都是IEnumerable<T>类型,而类库中几乎所有作为返回值的集合也都是IEnumerable<T>类型,所以基本上不需要改变原来的类库代码,我们就可以在原有的程序框架上使用Linq.

再看看我们现在的代码,还有一处不尽人意的地方,就是下面这段中的Result类

select new Result { City = g.Key, Count = g.Count() };

在我的应用程序中并不需要一个叫做Result的类,程序的所有逻辑也都和Result类无关.之所有我们写Result类仅仅是为了定义查询结果返回的数据结构和数据类型,那么假设我的程序中有几百种返回值不同的查询,那么我就需要写几百个类来定义这些查询结果的类型,这种做法非常麻烦繁琐.所以我们来改写两处: a)删除Result类的定义和 b)select子句中的Result类型.

select new { City = g.Key, Count = g.Count() };

这个新特性的关键就是上面这种格式代码的,这种新特性叫做匿名类型.具体实现的过程大致是:当你没有指定一个对象初始化器的类型,那么编译器识别到了对象初始化器后会自动指定一个类型,并为这个类型取一个编译时类型名,当然这个编译时类型名你既不需要知道,也不可能知道.关键是这种新特性使大家不再需要为一些与程序逻辑和程序概念无关的对象创建单独的类,就像在这里,我们就完全没有必要仅仅为一个查询结果特意定义一个类型.

到现在,我们的程序还有一点是很不现实的,大多数时候程序是从外部数据源读取出数据,而不是将数据写在程序中.那么回到LoadCustomers方法中,我们去掉对customers集合的定义.在项目的DEBUG目录中,我们准备了一个名叫Customers的XML文档,该文档记录了所有的客户信息,那么接下来让我们来看看如何从XML文档中检索出这些客户信息.
要完成这个,我们需要LINQ的一个组成部分叫做LINQ to XML,那么我们就使用XLinq来读取这些数据.
(注:参考工程项目代码Part III Types of Datasources demo 1st. XML Datasource)

static IEnumerable<Customer> LoadCustomers()
{
    var customers = from x in XElement.Load("Customers.xml").Elements("Customers")
    select new Customer
                    {
                        ContactName = x.Element("ContactName").Value,
                        City = x.Element("FullAddress").Element("City").Value
                    };
    return customers;
}

我们还是使用查询,从Customers.xml中取得所有叫做Customers的元素,然后使用select创建出每一个Customer对象,并使用XML中查询出来的值初始化Customer对象的各个属性.然后返回这个customers集合.如果我们现在再运行程序,会发现结果根本没有任何改变,唯一不同的是这次运行,程序的数据不是我们手工写在代码里,而是从XML中读取出来的.
这段代码我们使用XLinq来解析一个XML文档,取出数据,并根据这些数据来创建对象集合.而这样的工作现在只须简单的一行代码就可以完成,我们没必要再为繁琐的XML读取和不断的创建对象而发愁了,工作变得简单,顺畅.
还有一种常见的数据来源:强类型数据集.假设程序的底层有另一个应用程序为我们现在的这个程序提供数据,而低层提供给我们的数据存储在一个数据集中,那么就需要我们使用Linq来从数据集中查询数据.
那么我们写段代码来实现这项操作.
(注:参考工程项目代码Part III Types of Datasources demo 2nd Typed Dataset Datasource)

static CustomersDataSet.CustomersDataTable LoadCustomers()
{
    SqlDataAdapter adapter = new SqlDataAdapter
                                                 (
                                                     "SELECT * FROM Customers",
                                                     @"Data Source=.\SQLEXPRESS;" +
                                                     @"AttachDbFilename=|DataDirectory|\NORTHWND.MDF;" +
                                                     @"Integrated Security=True;" +
                                                     @"User Instance=True"
                                                 );
    CustomersDataSet.CustomersDataTable table = new CustomersDataSet.CustomersDataTable();
    adapter.Fill(table);
    return table;
}

这一段是一段非常简单的方法.我们构造了一个数据适配器,指定了数据适配器的查询语句和连接字符串.之后创建了一个数据集中的数据表对象,使用数据适配器填充了数据表,最后返回这张表.
那么运行结果和前两次都是一样的,但是这一次数据是从数据集中取出来的.
这里希望大家留意一下,虽然我不断的修改LoadCustomers方法,不过我查询的语句一直没有修改过,无论我是从手工定义的对象结合中取数据,或者从XML中取数据,还是从数据集中取数据,Linq查询语句一直都保持不变,我们只需要修改数据来源.

那么这些是现实中的程序的做法,但是在理想化的情况下,我们并不想使用这些中间数据源,不想记忆那些无聊的数据集图式结构;相反我们有时候希望可以直接传递SQL查询,这样一来我们可以省掉中间机制直接与数据库交互.那么我们去掉LoadCustomers方法,来一步一步试一下新的做法.
(注:参考工程项目代码Part IV DLinq demo 1 Custom Model)
首先,我们使用Northwind数据库,Northwind中有一张Customers表,而我们的程序中有一个Customer类,那么我们现在想做的是告诉.NET框架,我们希望将Customer类对象的数据存储在Customers表中.

[Table(Name="Customers")]
class Customer
{
    [Column]
    public string CustomerID { get; set; }
    [Column]
    public string ContactName { get; set; }
    [Column]
    public string City { get; set; }
}

这里我们使用自定义标记来告诉.NET框架这种信息.首先我们希望Customer类的对象存储在Customers表中,所以加一个Name属性来标记表名.然后类中的每一个属性对应库中表的一个列,这里因为属性名和列名一致,所以我们不需要额外标记.这样就完成了类结构和表结构的映射.
完成了映射之后,我们就可以通过连接字符串构造一个数据上下文(DataContext)对象来连接到数据库,然后我们使用这个对象来获取数据源,也就是Customers表.
具体的实现代码如下:

static void Main(string[] args)
{
    DataContext db = new DataContext
                                     (
                                         @"Data Source=.\SQLEXPRESS;" +
                                         @"AttachDbFilename=|DataDirectory|\NORTHWND.MDF;" +
                                         @"Integrated Security=True;" +
                                         @"User Instance=True"
                                     );
    var results = from c in db.GetTable<Customer>()
                       where c.City.StartsWith("M")
                       group c by c.City into g
                       select new { City = g.Key, Count = g.Count() };
    ObjectDumper.Write(results);
}

如果现在我们再来运行,结果依然和之前的一样,而不同的是这一次我们的数据是直接从数据库中检索出来的.为了大家可以更清晰的看到数据从数据库是如何查询出来的,我们在刚刚的代码中加上一句话:

db.Log = Console.Out;

这样,传递给数据库的SQL语句便会打印在屏幕上.
这里特意说明一下,实际上这次查询和我之前跟大家说得查询时完全矛盾的.记得之前我和大家解释了一下IEnumerable<T>,比如我们之前使用的LoadCustomers方法返回值类型为IEnumerable<T>.所以当我们使用var results = from c in LoadCustomers()时,Linq便在这个IEnumerable<T>中进行遍历查询.但是这次当我们使用了var results = from c in db.GetTable<Customer>()时,Linq却发送了一个SQL语句给数据库.这种矛盾的原因在于LoadCustomers的返回值类型是IEnumerable<T>,而db.GetTable<Customer>()返回值类型却不是IEnumerable<T>,而是IQueryable<T>.编译器发现结果是IQueryable<T>,便不会再遍历IEnumerable<T>,相反,编译器会将整个Linq查询语句转换成对象模型来代表查询,然后将对象模型作为IQueryable<T>的具体实现.从编译器角度来看,编译器并不清楚当前正在执行的是在LINQ to SQL;你可以创建一个你自己的IQueryable<T>的具体实现,那么当编译器发现了IQueryable<T>被载入,编译器便会接受查询语句,根据查询语句创建对象模型,最后将对象模型交给你编写的IQueryable<T>的具体实现.
那么LINQ to SQL在幕后作了什么呢?LINQ to SQL实际上接收对象模型,操纵对象模型,创建SQL语句,提交SQL查询到数据库,接收数据库返回的数据,并根据这些数据创建对象,返回对象给Linq查询结果.当然你可以创建自己的IQuerable<T>具体实现来完成完全不同的功能.

(注:参考工程项目代码Part V Dlinq demo 2 dbml Model)
不过,就像我经常挂在嘴边的一句话:这段代码中还有一些令人不满意的地方,那么我们现在的代码还是有一些不足之处.1)就是Customer类的定义中,我不希望每一列都要一个一个的手工添加自定义标记,这些是我们非常不喜欢做的事情,所以我们把Customer类的地方全部去掉;2)我想大家都不希望自己手工指定一个数据库连接字符串吧?大家应该都比较喜欢更亲切一点的数据库连接方法.所以我们把DataContext的实例化代码也去掉;3)当然我们也不希望每次都要写GetTable<Customer>(),我们希望的是当我使用db打个”.”后,智能提示会自动将所有可用的表名都给我提供出来.所以我们也去掉GetTable<Customer>().
那么接下来的情景大概是这样的:我们有一个数据库,而我们希望不写任何辅助代码的情况下,我就可以直接在程序中访问数据库.所以我们在项目中添加一个Linq to SQL文件,将它命名为Northwind.这时会看到一个设计界面,在设计界面中,可以直接将数据库的表拖入左窗口中.
大家应该留意一下,当我们将两张有关系的表拖进设计视图时,系统会自动设计好关系.所以在代码中就可以直接使用这两者之间的关系.
那么这个dbml文件到底作了什么呢?这个文件建立了一系列对象模型来对应数据库和数据库中的表.所以有了这个文件之后,便可以直接回到代码中直接使用这些对象.

static void Main(string[] args)
{
    var db = new NorthwindDataContext();
    var results = from c in db.Customers
                       where c.City.StartsWith("M")
                       group c by c.City into g
                       select new { City = g.Key, Count = g.Count() };
    ObjectDumper.Write(results);
}

这时测试,依然正常运行,而且运行结果和之前一样.那么这样的代码才是大家平时工作的时候希望写的代码.比如你正在写程序,可是突然需要查询,而你根本没有意识到这一点,继续写代码,因为查询已经变成了平常的普通代码了,有了Linq我想我们已经离这个目标很近了.
那么下一个话题来看看存储过程.
(注:参考工程项目代码Part VI Dlinq demo 3 Stored Precedure)
回到dbml设计窗口我们可以将数据库中的存储过程直接拖放到右窗口.
这是回到代码中,可以非常简单的直接通过db对象来调用存储过程.

static void Main(string[] args)
{
    var db = new NorthwindDataContext();
    var results = db.Customers_By_City("London");
    ObjectDumper.Write(results);
}

这样的代码就是我们期望的代码,当需要什么的时候,直接使用数据库调用,就像调用方法一样传进去参数就可以了,一切流畅顺利.

到这里先停一下,来回头看看刚刚谈过的都有什么:1)创建了一个C#2.0的应用程序,2)经2.0应用程序转化成了C#3.0,3)在应用程序中我们添加了Linq查询,4)从普通的集合中读取数据,5)从XML文档中读取数据,6)从数据集中读取数据,7)从数据库中读取数据.

注:今天就先到这里.第二部分内容 LINQ to XML 下次再为大家放出.

 

朱宇
2007.7.4

Feedback

#1楼    回复  引用  查看    

2007-07-04 12:18 by 木野狐      
谢谢

#2楼    回复  引用  查看    

2007-07-04 12:29 by 枫崖      
好像现在中文版的还没有发布吧
我装的是中文版的visual studio能不能用呢?

#3楼    回复  引用  查看    

2007-07-04 12:53 by MYOOP      
不错。赫赫

#4楼    回复  引用  查看    

2007-07-04 12:55 by 金色海洋(jyk)      
还是没有SQL语句直接,另外怎么还是要写代码呢?

#5楼    回复  引用  查看    

2007-07-04 13:06 by 冬冬      
赞,最近想学C#3.0,谢谢LZ的这篇文章,相当精致,受益匪浅,期待下篇。

#6楼    回复  引用    

2007-07-04 13:07 by 高海东 [未注册用户]
你的这个颜色让别人看的难受

#7楼    回复  引用  查看    

2007-07-04 14:39 by 紫色阴影      
最好给个webcast观看链接

#8楼    回复  引用  查看    

2007-07-04 16:42 by 随风流月      
颜色不够好。并且希望能够得到原 Webcast 链接。

#9楼    回复  引用  查看    

2007-07-04 16:43 by YAO.NET(三千)℡      
颜色还可以,也是想知道链接.

#10楼    回复  引用  查看    

2007-07-04 17:01 by aspnetx      
不错,支持下
建议给出webcast下载链接

#11楼    回复  引用  查看    

2007-07-04 21:38 by Boler Guo      
Maybe in here:
http://blogs.msdn.com/charlie/archive/2007/06/18/videos.aspx

#12楼    回复  引用  查看    

2007-07-04 22:22 by 吾跃乾坤      
晕哦,我才开始搞 2.0

#13楼    回复  引用  查看    

2007-07-05 08:17 by 随风流月      
@金色海洋(jyk)
难道可以不写代码?

#14楼    回复  引用  查看    

2007-07-05 08:35 by CooS      
非常不错

#15楼    回复  引用  查看    

2007-07-05 09:37 by Jeffers Yuan      
收藏下来,晚上看看!

#16楼    回复  引用  查看    

2007-07-05 15:01 by 丁一      
翻译的很好.. 多谢...

#17楼    回复  引用  查看    

2007-07-06 08:27 by ColdDog      
确实非常不错,看来2.0中万人举颂的ORM,似乎可以告一段落了~当然,不是完全拒绝ORM,至少DLINQ可以冲击相当一部分所谓ORM的架构~

另外问一下,现在的c#3.0支持的环境是什么呢?VS2005中文版+VS2005扩展可以吗?

#18楼 [楼主]   回复  引用  查看    

2007-07-06 10:17 by KENNETHBYRON      
@ColdDog
恩.VS2005+CTP就可以使用C#3.0 DLinq 和 Linq2Entities.
不过这样一来你下两个CTP.并且这些CTP和VS Orcas Beta1中的还不完全一样.
建议下一个C# Oracs Express

#19楼    回复  引用  查看    

2007-07-06 10:21 by ColdDog      
@KENNETHBYRON
呵呵,我在问下,C# Oracs Express是不是就是号称6G的那个家伙阿?还有下载地址呢?6G的下载地址我有,太可怕了

[7.7]非常感谢,下回来了~呵呵

#20楼 [楼主]   回复  引用  查看    

2007-07-06 22:08 by KENNETHBYRON      
@ColdDog
不是的.你说的5.9G的是完整版的VS Orcas Beta1.
你可以下Express版的.下面是地址:
VS Orcas Express