[翻译]Pro C# 2008 and the .NET 3.5 Platform, Fourth Edition 第二十四章 LINQ API编程(三)
本章翻译目的为学习之用,英文版权属原作者所有,中文版权根据turingbook的贴子归图灵,如有侵权,请告知删除,禁止用于商业用途!在此特别鸣谢图灵的傅老师(本章翻译的贴出征得她的同意)和杨福川(为大家介绍了这本好书)。本人翻译水平有限,错误之处难免望大家批评指正!本文仅摘出一部分。
LINQ to SQL编程
LINQ to SQL是一种API,它把规范的LINQ查询表达式运用到关系型数据库中。LINQ to SQL提供多种类型(在System.Data.Linq.dll数据集中),促进了代码和物理数据库引擎之间的沟通。
LINQ to SQL的主要目标是维持关系型数据库和与之交互的编程逻辑之间的一致性。例如,使用强类型LINQ查询胜于用一大串的字符来表示数据库查询。同时,用标准的面向对象编程技术和数据交互,胜于把关系型数据作为记录流来处理。假定LINQ to SQL允许在C#代码基中直接集成数据访问,那么需要手工建立多个自定义类和隐藏ADO.NET实现的数据访问库的代码量就会极大地缩小。
在用LINQ to SQL编程过程中,看不到任何使用ADO.NET类的痕迹,比如有SqlConnection,SqlCommand,或者SqlDataAdapter。用LINQ查询表达式,实体类(被简单定义)和DataContext类,就能够实现所有预期的数据操作(增删改查),同时定义事务上下文,定义新数据库实例(或者是整个数据库),调用存储过程,和处理其他数据库为核心的活动。
甚者,LINQ to SQL类(像DataContext)可被开发成为集成到标准的ADO.NET数据类型。例如,一个DataContext重载的构造函数和输入一个IDbConnection相关的对象,它调用ADO.NET链接对象支持的公共接口。在这种方式下,已有的ADO.NET数据访问库可以和C#2008 LINQ查询表达式集成,反之亦然。实际上,正如微软关注的那样,LINQ to SQL是ADO.NET家庭中的新成员。
......
DataContext类作用
一旦定义了实体类,然后就可以用DataContext类来传递查询表达式给关系数据型库。与LINQ to SQL相关的类型主要负责把LINQ查询表达式翻译成恰当的SQL查询同时与特定的数据库交互。在某些方面,DataContext看起来就像一个ADO.NET链接对象,因为它需要一个链接字符串。但是,和典型的链接对象不同的是,DataContext类型有多个成员,它们会把查询表达式的结果与特定的实体类进行匹配。
甚者,DataContext类型定义工厂模式来获取代码中定义的实体类实例。一旦获取实体类的实例,就可以根据需要自由地去改变它的状态,比如添加和更新记录,并提交修改的对象用于处理。在这种情况下,DataContext与ADO.NET数据适配器类型非常相似。
一个简单的LINQ to SQL例子
在进行进一步的研究之前,看一个运用LINQ to SQL和第22章创建的AutoLot数据库库存表交互的例子。这个例子中,不会用到AutoLotDAL.dll库,取而代之的是手工编码。创建一个新的控制台程序命名为SimpleLinqToSqlApp并引用System.Data.Linq.dll数据集。
然后,插入一个新的C#类文件并命名为Inventory.cs。该文件定义我们的实体类,它需要配备多个以LINQ为核心的属性;请确保已经导入了System.Data.Linq.Mapping和System.Data.Linq命名空间。当一切准备就绪,下面就是Inventory类的定义:
[Table] public class Inventory { [Column] public string Make; [Column] public string Color; [Column] public string PetName; // Identify the primary key. [Column(IsPrimaryKey = true)] public int CarID; public override string ToString() { return string.Format("ID = {0}; Make = {1}; Color = {2}; PetName = {3}", CarID, Make.Trim(), Color.Trim(), PetName.Trim()); } }} } |
首先请注意实体类中带有[Table]属性的修饰符,同时每个公有字段都用[Column]标识。在这两种情况下,变量名是直接和物理数据库表对应的。但是,要求并非十分苛刻,就像TableAttribute和ColumnAttribute类型都支持名称属性那样,它允许你把物理表和数据表分离。同时也该注意到CarID字段通过变量名属性语法设置ColumnAttribute值为IsPrimaryKey。
简单地说,每个字段都被声明为公共的。如果需要更强的封装,你大可用公共参数封装私有字段(你也可以选择自动属性)。如果你这样做,它就会成为参数,而非字段,该参数的属性由[Column]标识。
值得指出的是一个实体类可以包含多个成员,这些成员并不需要和它表示的数据表一一对应。正如LINQ运行时关注的那样,只有用LINQ to SQL属性标识的项才会在数据交换中所应用。例如,这里的Inventory类定义提供ToString()的自定义实现,运行程序快速的显示它的状态。
既然已经有一个实体类,我们可以使用DataContext类型去提交(和翻译) LINQ查询表达式给特定的数据库。考虑接下来的Main()方法,它将显示Inventory表中由AutoLot数据库保存的所有条目结果。
class Program { const string cnStr = @"Data Source=(local)"SQLEXPRESS;Initial Catalog=AutoLot;" + "Integrated Security=True"; static void Main(string[] args) { Console.WriteLine("***** LINQ to SQL Sample App *****"n"); //创建一个 DataContext 对象. DataContext db = new DataContext(cnStr); // 现在创建 Table<> 类型 Table<Inventory> invTable = db.GetTable<Inventory>(); // 用LINQ 查询显示所有数据 Console.WriteLine("-> Contents of Inventory Table from AutoLot database:"n"); foreach (var car in from c in invTable select c) Console.WriteLine(car.ToString()); Console.ReadLine(); } } |
注意在创建DataContext类型的时候,需要植入一个合适的链接字符串,它表示的是一个简单的字符串常量。当然,可把它存储到应用配置文件或者使用SqlConnectionStringBuilder类型,在一种更加面向对象的方式中,来处理该字符串类型。
接下来,通过调用DataContext类中的泛型函数GetTable<T>,获取一个Inventory实体类的实例,所以这样做是把实体类定义成类型参数。最后,我们创建一个LINQ查询表达式并且把它应用到invTable对象。就如预期的那样,最终结果是一个显示Inventory所有条目的表。