DLinq概述(2)

      构建一个DLinq应用的第一件事情就是声明一个包含应用程序数据定义的对象类。那让我们来开始这个简单的应用。

 创建实体类

我们创建一个简单的Customer,关联SQL Server范例数据库Northwind的customers表。

[Table(Name="Customers")]
public classCustomer
{
      
publicstring CustomerID;

       public string City;
}

我们使用Customer类的Table特性的Name属性来标识数据库表的名称。如果Name属性没有被指定,DLinq默认是一个类的名称作为数据库表的名称。只有拥有Table特性的实例才能被保存到数据库中。这样的类被称之为实体类(entity classes),而它们的实例称之为实体(entities)

除了关联实体类到数据库表,您还需要指定实体类的属性或者成员和数据库表列之间的映射关系。DLinq提供了一个Column特性来声明这种映射关系。

[Table(Name="Customers")]
public classCustomer
{
       [
Column(Id=true)]
      
publicstring CustomerID;

       [Column]
      
public string City;
}

您可以使用Column特性的一系列属性来精确地定义实体类成员或者属性到数据表列的映射。其中的Id属性标识实体类成员或者属性对应数据库表格中的主键。类似声明Table特性,您仅仅需要通过Column特性提供足够多的信息来让DLinq推导出数据库表格的列和实体类的属性或成员之间的映射关系。在这个例子里面,您需要告诉DLinq实体类Customer中的CustomerID成员是表格customers的主键,而不需要指定Column特性的name和type属性。只有映射成数据库表格列的成员或者属性值才能被持久化或者从数据库中读取。而实体类的其他成员或者属性则被为与数据库无关的应用逻辑。

DataContext

DataContext是数据库和应用程序的中间层,它负责从数据库中查询数据并返回对象给应用程序,同时它能够把应用程序中实体类的改变反映到数据库中。DataContext的使用方式类似ADO.NET中的Connection。事实上DataContext就是用一个ADO.NETConnection对象或者一个连接字符串类来进行初始化的。DataContext的任务就是把应用程序对对象集合的查询翻译成SQL查询,然后根据从数据库中读取的数据来构建对象返回给应用程序。DataContext使得使用标准查询操作符(Standard Query Operators),如WhereSelect,来实现语言集成查询成为可能。

例如,您可以按照下面的方式,使用DataContext来获取所在城市为London的所有Customer实体

// 使用连接字符串构建DataContext
DataContext db = new       DataContext("c:""northwind""northwnd.mdf");

// 通过查询获取实体集合
Table<Customer> Customers = db.GetTable<Customer>();

// 查询所在城市为London的所有Customer实体
var q =
      
from c in Customers
      
where c.City == "London"
       select c;

foreach (var cust in q)
      
Console.WriteLine("id = {0}, City = {1}", cust.CustomerID, cust.City);

每个数据库可以视为表格的集合,通过DataContext的GetTable()方法可以获取到指定表格行对应的所有实体。我们推荐您不要使用原始DataContext类提供的GetTable()方法,而是使用强类型的DataContext。一个强类型的DataContext声明了一个Table集合为DataContext的成员。

publicpartialclassNorthwind : DataContext
{
      
publicTable<Customer> Customers;
      
publicTable<Order> Orders;

       publicNorthwind(string connection): base(connection) {}
}

查询所在城市为London的所有的Customer实体,可以使用下面更加简单的方式:

Northwind db = newNorthwind("c:""northwind""northwnd.mdf");

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

foreach (var cust in q)
      
Console.WriteLine("id = {0}, City = {1}",cust.CustomerID, cust.City);

在本文下面的章节中,我们将继续使用强类型的Northwind类。

定义关系

关系数据库中的关系模型是一个表格的外键关联另外一个表格的主键。为了浏览两个表格的数据,您需要显示地使用连接关系操作来进行查询。而另一方面,对象则是使用属性来引用另外一个对象或者对象集合,所以只需要”dot”符号就可以浏览另外一个对象或者对象集合。很显然,”dot”操作要比连接关系操作要简单,因为您没必要每次浏览两个表格的数据的时候都需要根据复杂的连接条件来进行查询。

使用引用属性的方式把相关联的对象或者对象集合封装在您的实体类里面是相当方便的。DLinq提供了用于定义了实体类成员或者属性的Association特性,您可以使用它来标识两个实体之间的关系。实体之间的这种连接关系(association relationship与数据库表格之间的外键-主键关系很相似。

[Table(Name="Customers")]
public classCustomer
{
       [
Column(Id=true)]
      
publicstring CustomerID;
       ...

       privateEntitySet<Order> _Orders;

       [Association(Storage="_Orders", OtherKey="CustomerID")]
      
publicEntitySet<Order> Orders {
             
get { returnthis._Orders; }
             
set { this._Orders.Assign(value); }
       }
}

现在Customer类有一个属性Orders来声明customersorders表之间的关系。因为customersorders的关系是一对多的,所以Orders属性的类型被声明为EntitySet。我们使用Association特性的OtherKey来描述customersorders之间的关系。相对这个实体类,与之相关联的实体类中也需要指定相应的属性名称。在这里,我们并没有指定ThisKey属性。在一般情况下,我们使用ThisKey来指关系This端的成员。尽管如此,我们可以让DLinq根据指定为主键的成员来推导出ThisKey

请注意我们是怎样在Order类中声明对应的特性的。

[Table(Name="Orders")]
public classOrder
{
       [
Column(Id=true)]
      
publicint OrderID;

       [Column]
      
publicstring CustomerID;

       privateEntityRef<Customer> _Customer;   

       [Association(Storage="_Customer", ThisKey="CustomerID")]
      
publicCustomer Customer {
             
get { returnthis._Customer.Entity; }
             
set { this._Customer.Entity = value; }
       }
}

Order类使用EntityRef是描述到customers表的关系。使用EntityRef的类需要支持延迟加载(deferred loading(后面会提到)。我们指定了Customer属性的Association特性的ThisKey属性,因为DLinq无法推断出关系This端的成员。

我们也指定了Association特性的Storage属性。它告诉DLinq哪个私有成员拥有属性值。这就可以让DLinq不通过公共属性而是通过成员来存取它们的值。如果您想要DLinq不去关注在属性获取或者设置函数里面的应用程序逻辑,这是非常必要的。如果Storage属性没有被显示地指定,DLinq则使用公共属性来进行数据的存取。您也可以使用Column特性的Storage属性来做类似的声明。

一旦您打算在您的实体类之间引入关系,您可能需要获取属性变化通知以及保持数据的一致性,因此您需要编写更多的代码。不过很幸运的是,这儿有一个工具(后面会提到)可以帮助您自动实现这些定义,它们被封装在一个part类里,在这个类里您可以加入自定义的业务逻辑。

对于本文的后续内容,假设我们已经通过这个工具产生了一个完整的Northwind的定义和所有的实体类。

关系查询

既然您已经声明了实体类之间的关系,那么现在您就可以通过引用其中的关系成员或者属性来实现简单的查询。

var q =
      
from c in db.Customers
      
from o in c.Orders
      
where c.City == "London"
       selectnew { c, o };

上面的查询使用Customer的Orders属性来产生了一个新的CustomerOrder序列。

       下面的查询实现了同上面同样的效果。

var q =
      
from o in db.Orders
      
where o.Customer.City == "London"
       selectnew { c = o.Customer, o };

在上面这个例子中,我们查询所有的Orders,然后通过OrderCustomer关系字段来获取到相关联的Customer对象。

修改和保存实体

很少有应用程序只用到了查询。实体可能被创建或者修改。DLinq的一个设计目标就是足够灵活地处理和持久化实体的变化。通过查询或者创建而得到的一个实体,只要它是可用的,您就可以像使用普通对象操作它,修改它的值或者把它添加到一个集合或者从一个集合中删除。DLinq都会跟踪实体的这些变化,一旦您调用了提交命令,DLinq就会把这些变化提交到数据库。

在下面的例子中,我们使用自动生成工具,根据样例数据库Northwind得到了CustomerOrder实体类的定义。实体类的定义在这里就不做介绍了。

Northwind db = new Northwind("c:""northwind""northwnd.mdf");

// 获取特定的customer
string id = "ALFKI";
var cust = db.Customers.Single(c => c.CustomerID == id);

// 修改Contact Name
cust.ContactName = "New Contact";

// 删除一个已有的Order
Order ord0 = cust.Orders[0];

// Orders集合中移除上面得到的Order
db.Orders.Remove(ord0);

// 创建一个Order并将之添加到Order集合中
Order ord = newOrder { OrderDate = DateTime.Now };
cust.Orders.Add(ord);

// 使用DataContext提交所有变化
db.SubmitChanges();

一旦SubmitChanges()被调用,DLinq就会自动产生并且执行相应的SQL命令,并把这些变化提交到数据库中。当然,我们也可以通过直接调用SQL命令来实现同样的功能。

posted @ 2007-10-14 17:45  RicoRui  阅读(640)  评论(0编辑  收藏  举报