在LINQ to SQL一节中也讲过关于LINQ to SQL与ADO.net Entity Framework的区别,以及两者分别在什么情况下使用。ADO.net Entity Framework是在LINQ to SQL之后产生的,现在还处在bata3版本中。微软很早就已经在准备设计实现自己的ORM框架,所以有的人认为LINQ to SQL的出现,主要是因为微软ORM框架久久未能实现,所以先做了一个简单的版本,一方面满足用户的需求,另一方面想全面的ORM框架过渡。不论这种说法争取与否,ADO.net Entity Framework在ORM实现框架上的却是比LINQ to SQL更加完善。ADO.net Entity Framework(以后简称AEF)用C(概念层)S(存储层)M(映射层)三层结构实现了ORM中的C(概念层)L(逻辑层)P(数据存储层)三层结构。AEF的三层结构比ORM的三层概念型的结构更加实用。用户可以不用关心SM层(这些可以由Visual Studio设计工具自动生成,当然,如果知道具体细节,则可操作空间会更大),直接对C层进行操作,在C层,系统自动将数据库表中的行映射成为实体对象,和LINQ to SQL类似,数据库对应实体对象名为<数据库名>Entities(在LINQ to SQL中为<数据库名>DataContext),行数据实体类名和对应的表名一致,如数据库中的Order表会对应成Orders实体类,其中表名后是否加s(即对应成Order类还是Orders类)可以在Visual Studio 中设置。
当定义好 Entity Data Model 的 CS/MS/SS 之后,即可以利用 ADO.NET Entity Framework 的用户端来访问 EDM,EDM 中的数据提供者会向数据来源访问数据,再传回用户端。
从上图可以看出,目前 ADO.NET Entity Framework 有三种用户端:
Entity Client
Entity Client 是 ADO.NET Entity Framework 中的本地用户端 (Native Client),它的对象模型和 ADO.NET 的其他用户端非常相似,一样有 Connection, Command, DataReader 等对象,但最大的差异就是,它有自己的 SQL 指令 (Entity SQL),可以用 SQL 的方式访问 EDM,简单的说,就是把 EDM 当成一个实体数据库。
程序5-1
// Initialize the EntityConnectionStringBuilder.
EntityConnectionStringBuilder entityBuilder = new EntityConnectionStringBuilder();
//Set the provider name.
entityBuilder.Provider = providerName;
// Set the provider-specific connection string.
entityBuilder.ProviderConnectionString = providerString;
// Set the Metadata location.
entityBuilder.Metadata = @"res://*/AdventureWorksModel.csdl|
res://*/AdventureWorksModel.ssdl|
res://*/AdventureWorksModel.msl";
Console.WriteLine(entityBuilder.ToString());
using (EntityConnection conn = new EntityConnection(entityBuilder.ToString()))
{
conn.Open();
Console.WriteLine("Just testing the connection.");
conn.Close();
}
Object Context
由于 Entity Client 太过于制式,而且也不太符合 ORM 的精神,因此微软在 Entity Client 的上层加上了一个供编程语言直接访问的界面,它可以把 EDM 当成对象般的访问,此界面即为 Object Context (Object Service)。
在 Object Context 中对 EDM 的任何动作,都会被自动转换成 Entity SQL 送到 EDM 中执行。
程序5-2
// Get the contacts with the specified name.
ObjectQuery<Contact> contactQuery = context.Contact
.Where("it.LastName = @ln AND it.FirstName = @fn",
new ObjectParameter("ln", lastName),
new ObjectParameter("fn", firstName));
LINQ to Entities
Object Context 将 EDM 的访问改变为一种对对象集合的访问方式,这也就让 LINQ 有了发挥的空间,因此 LINQ to Entities 也就由此而生,简单的说,就是利用 LINQ 来访问 EDM,让 LINQ 的功能可以在数据库中发挥。
程序5-3
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<Product> productNames =
from p in products
select p;
}
Entity SQL
在介绍Entity Client时,提到了Entity SQL,在这里做一下简单的介绍:
Entity SQL(以后简称ESQL)和传统的SQL十分类似,但仍有些地方不同:
ESQL中没有*(即传统SQL中的SELECT * FROM...),但选择对象的所有属性时,可以使用VALUE,
如: SELECT VALUE c FROM NorthwindEntities.Customers AS c
在FROM区域,ESQL所提取数据的地方是EDM(Entity Data Model),此处必须是一个Entity class,且包含Entity Container名称,如上例所示
下面介绍一下ESQL常用的关键字:
TOP:和SQL类似,如: SELECT VALUE TOP(5) c FROM NorthwindEntities.Customers AS c
SKIP:SELECT VALUE c FROM NorthwindEntities.Customers AS c
ORDER BY c.CustomerID SKIP(5)
注意:SKIP是放在整个查询表达式之后的,并且,SKIP必须和ORDER BY合用才行
LIMIT: LIMIT用来限制回传结果集的条数,类似与TOP,同样是取结果集的前N条数据,但是LIMIT必须与ORDER BY合用,同样,LIMIT也可以和SKIP合用
如:SELECT VALUE c FROM NorthwindEntities.Customers AS c
ORDER BY c.CustomerID SKIP(5) LIMIT(5)
WHERE、LIKE、IN、BETWEEN、GROUP BY、JOIN:都和传统SQL使用方式相同,所以不再特别介绍
像SQL语句一样,ESQL也有很多内置的函数,详细信息可查阅MSDN
ADO.net Entity Framework的使用
由于ADO.net Entity Framework的基本操作和LINQ to SQL十分类似,所以我们可以参照LINQ to SQL来操作ADO.net Entity Framework。下面主要介绍一下LINQ to SQL和ADO.net Entity Framework在应用中的不同之处
数据的更新:数据操作不外乎C(Create)R(Receive)U(Update)D(Delete)四个大项,其中R的角色由LINQ Expression执行,LINQ Expression的使用上,LINQ to SQL和AEF基本一样,剩下的CUD则是由DataContext(LINQ to SQL)或Entities(AEF)来执行:
1.Create:LINQ to SQL中采用Entity Object调用InsertOnSubmit方法进行插入,而在AEF中,设计工具会自动在数据库实体类中为每个表添加一个AddTo<Entity class>的方法,如AddToOrders,而这个方法只是一个Proxy,实际是间接调用数据库对象的AddObject方法
LINQ to SQL
ADO.net Entity Framework
程序5-5
/// <summary>
/// 插入
/// </summary>
public void Insert()
{
using (NorthwindEntities northwind = new NorthwindEntities())
{
Orders order = Orders.CreateOrders(northwind.Orders.Count(), "Suzhou");
// 或 Orders order = new Orders() { OrderID = northwind.Orders.Count() + 1, ShipCountry = "Suzhou" };
northwind.AddToOrders(order);
// 或 northwind.AddObject("Orders", order);
northwind.SaveChanges();
}
}
2.Delete LINQ to SQL
程序5-6
// Query the database for the rows to be deleted.
var deleteOrderDetails =
from details in db.OrderDetails
where details.OrderID == 11000
select details;
foreach (var detail in deleteOrderDetails)
{
db.OrderDetails.DeleteOnSubmit(detail);
}
try
{
db.SubmitChanges();
}
catch (Exception e)
{
Console.WriteLine(e);
// Provide for exceptions.
}
ADO.net Entity Framework
程序5-7
/// <summary>
/// 删除
/// </summary>
public void Delete()
{
using (NorthwindEntities northwind = new NorthwindEntities())
{
int maxID = 3;
Orders order = northwind.Orders.First<Orders>(od => od.OrderID > maxID);
if (null == order)
return;
northwind.DeleteObject(order);
northwind.SaveChanges();
// 或
var orders = from od in northwind.Orders
where od.OrderID > maxID
select od;
if (null == orders)
return;
foreach (var orderDel in orders)
{
northwind.DeleteObject(orderDel);
}
northwind.SaveChanges();
}
}
3.Update LINQ to SQL 和 ADO.net Entity Framework的更新基本一样,所以不再分别列出,下面以AEF为例
程序5-8
/**//// <summary>
/// 修改
/// </summary>
public void Modify()
{
using (NorthwindEntities northwind = new NorthwindEntities())
{
Orders order = northwind.Orders.First<Orders>(od => od.OrderID > 2);
// 或 Orders order = northwind.Orders.Where<Orders>(od => od.OrderID < 5).First();
if (null == order)
return;
order.OrderID = 3;
order.OrderDate = DateTime.Now;
order.ShipCountry = "Suzhou";
northwind.SaveChanges();
}
}
在n-Tier中,有时Client调用了Service(如下面程序5-6中的GetObject方法)来取得对象,如果对象和ObjectContext没有了任何关联,其只是一个一般的对象,在客户端中我们先保存对象的原始值,然后对复制的对象进行修改,修改后通过Serveice(AttachNow)将数据异动回数据库,这时可以采用下例中的方法:
程序5-9
/**//// <summary>
/// Server:获取对象
/// </summary>
static NorthwindEntities GetObject()
{
Orders order;
using (NorthwindEntities northwind = new NorthwindEntities())
{
order = (from o in northwind.Orders
where o.OrderID > 5
select o).FirstOrDefault();
northwind.Detach(order);
}
return order;
}
/**//// <summary>
/// Server:将数据异动回数据库
/// </summary>
static void AttachNow(Orders order, Orders updateItem)
{
NorthwindEntities northwind = new NorthwindEntities();
northwind.Attach(item); // 将原始值与northwind建立关系
northwind.ApplyPropertyChanges("OrderID", updateItem); // 将变动值和原始值配对
northwind.SaveChanges(); // 更新回数据库
}
/**//// <summary>
/// Client:通过Server获取对象并更新,将更新结果异动回数据库
/// </summary>
static void Update()
{
NorthwindEntities.Orders order = GetObject();
NorthwindEntities.Orders updateItem = new NorthwindEntities.Orders();
updateItem.OrderID = order.OrderID;
updateItem.OrderDate = order.OrderDate;
updateItem.OrderDate = System.Date.Now;
AttachNow(order, updateItem);
}