6 SQL集成(SQL Integration)
.NET 语言级集成查询可以用来直接查询关系型的数据存储(relational data stores),而不用离开本地编程语言(local programming language)的语法或编译时环境(the syntax or compile-time environment)。这个技巧(This facility),代码名叫 DLinq(code-named DLinq),把 SQL schema 信息的集成(the integration of SQL schema information)的优势带入了(takes advantage of) CLR 元数据(metadata)。这个集成(integration)把 SQL 表(table)和视图(view)的定义编译进 CLR 类型,这样能够从任何语言上访问。
DLinq 定义了两个核心的属性(two core attributes),[Table] 和 [Column],它们指出(indicate)了哪个 CLR 类型和属性(which CLR types and properties)是符合(correspond to)外部的 SQL 数据(external SQL data)。[Table] 属性能被应用于(applied to)一个类并联合(associates) CLR 类型和一个指定的(named) SQL 表或视图。这两个属性都是参数化的(parameterized),以允许 SQL 型的元数据(SQL-specific metadata)得以保留(retained)。举例来说,考察下面这段 SQL schema 的定义:
create table People (
Name nvarchar(32) primary key not null,
Age int not null,
CanCode bit not null
)
create table Orders (
OrderID nvarchar(32) primary key not null,
Customer nvarchar(32) not null,
Amount int
)
与之对等(equivalent)的 CLR 定义看起来如下:
[Table(Name="People")]
public class Person {
[Column(DbType="nvarchar(32) not null", Id=true)]
public string Name;
[Column]
public int Age;
[Column]
public bool CanCode;
}
[Table(Name="Orders")]
public class Order {
[Column(DbType="nvarchar(32) not null", Id=true)]
public string OrderID;
[Column(DbType="nvarchar(32) not null")]
public string Customer;
[Column]
public int? Amount;
}
通过这个例子注意到,允许为 null (nullable)的 column 映射到(map to)CLR 里的允许为 null 的类型(允许为 null 的类型首次出现在 .NET Framework version 2),对 SQL 类型来说没有一个与之一一对应的(a 1:1 correspondence with)CLR 类型(比如nvarchar, char, text),原始的 SQL 类型在 CLR metadata 里被保留。
为发行一个查询而不是一个关系型存储(issue a query against a relational store),LINQ 模式的 Dlinq 实现把查询从它的表达树(expression tree)转换并编成(translates …form into)一个 SQL 表达试和适合远程赋值(suitable for remote evaluation)的 ADO.NET DbCommand 对象。例于考察如下简单的查询:
// establish a query context over ADO.NET sql connection
DataContext context = new DataContext(
"Initial Catalog=petdb;Integrated Security=sspi");
// grab variables that represent the remote tables that
// correspond to the Person and Order CLR types
Table<Person> custs = context.GetTable<Person>();
Table<Order> orders = context.GetTable<Order>();
// build the query
var query = from c in custs, o in orders
where o.Customer == c.Name
select new {
c.Name,
o.OrderID,
o.Amount,
c.Age
};
// execute the query
foreach (var item in query)
Console.WriteLine("{0} {1} {2} {3}",
item.Name, item.OrderID,
item.Amount, item.Age);
DataContext 类型提供了一个轻量级的转换器(lightweight translator),它的工作是把标准查询操作符(standard query operators)转换成 SQL。DataContext 使用现有的 ADO.NET IdbConnection 来访问存储(accessing the store),能够使用一个确定(established)的ADO.NET 连接对象或者一个可以用来创建一个连接的连接字符串(a connection string)的任一个来初始化(initialized)。
GetTable 方法提供 IEnumerable 兼容的变量(IEnumerable-compatible variables),能够被用在查询表达式(query expressions)里来代表(represent)远程的表或视图(the remote table or view)。调用 GetTable 不会导致任何与数据库的交互(interaction),更准确的说(rather)它们扮演(represent)the potential 通过时用查询表达式来与远程的表或视图相配合(interact with)。在我们上面的例子中,查询不会对存储(store)发送传输的影响(get transmitted to),直到程序迭代出(iterates over)查询表达式为止,在 C# 中使用 foreach 语句也是如此(in this case)。当程序开始迭代完成(iterates over)查询时,DataContext 机构(machinery)把查询表达式树(expression tree)转换成要发送给存储(sent to the store)的如下所示的 SQL 语句:
SELECT [t0].[Age], [t1].[Amount],
[t0].[Name], [t1].[OrderID]
FROM [Customers] AS [t0], [Orders] AS [t1]
WHERE [t1].[Customer] = [t0].[Name]
需要重点注意的是通过在本地编程语言(local programming language)中直接(directly)内建查询能力(building query capability),开发人员可以获得关系模型(the relational model)的完全能力(full power),而不用不得不静态地(statically)将关联烘烤进(bake the relationships into)CLR 类型中。成熟的 O/R 映射(Full-blown object/relational mapping)技术还能够为那些希望它泛函性(functionality)的用户利用(take advantage of)这个核心查询能力(core query capability)。
待续, 错误难免,请批评指正,译者Naven 2005-10-24