[ASP.NET
MVC 小牛之路]06 - 使用 Entity Framework
在家閒著也是閒著,繼續寫我的[ASP.NET MVC 小牛之路]系列吧。在系列的上一篇部落格文章中,在顯示書本資訊清單的時候,我們是在程式碼中手工造的資料。本文將示範如何在ASP.NET MVC中使用Entity
Framework從資料庫取得資料。雖然本文題目聽起來比較簡單,但如果你認真閱讀,相信你一定會有所收穫。
本文目錄:
ORM 和 EF
當我們要開發一個應用程序,就要考慮怎樣展示數據,怎樣持久化數據。考慮這個問題時我們所要關心的東西,最重要的莫過於程式的效能、開發的簡易性和程式碼的可維護、可擴展性。
持久化(Persistence),是指在應用程式中能永久保存各個處理狀態資訊的機制。如果沒有持久化這個機制,狀態只能保存在記憶體中,機器關機後就會遺失。
在應用程式中,當要永久保存資料時,我們會選擇關聯式資料庫(Relation DataBase);當要暫時保存資料時,我們則使用儲存在記憶體中的物件。目前對大多數開發人員來說,都是用關聯式資料庫技術來作為持久化機制。雖然現在有些人正在嘗試使用物件資料庫(Object DataBase)技術,但關聯式資料庫技術在許多年以內依然會是最主要的持久化機制。
為什麼會出現物件資料庫? SQL語言是一種非過程化的面向集合的解釋型語言,而許多高階語言是過程化的物件導向的編譯型語言,這使得兩種語言之間存在著不匹配,導致效率不如人意。這種不匹配被稱為“阻抗失配”,而物件資料庫的出現就是為了解決“阻抗失配”。
我們知道,在關聯式資料庫技術中是用Table以行和列的結構來存放和組織資料的。在.NET 2.0以前,C#還沒有泛型的時候,人們基本上用填充在DataSet中的DataTable來映射並存放從關係數據庫中查詢出來的數據,正如下面代碼所示:
using (SqlConnection conn = new SqlConnection(connString)) { using (SqlDataAdapter da = new SqlDataAdapter("Select * from order", conn)) { DataTable dt = new DataTable(); da.Fill(dt); ... } }
這種方式雖然能讓物件導向語言匹配關聯式資料庫,但它有一些明顯的缺點,如非類型安全、難以操控、低效能等。從.NET 2.0開始,人們開始透過泛型技術用實體模型物件的集合來匹配關係資料庫中的數據,這種方式解決了DataTable方式所面臨的缺點,且它有強類型、在VS中自動完成、編譯時檢查等特點,廠受.Net開發人員的喜愛。
為了讓開發人員不用手動去做這種「配對」工作,人們研發了許多ORM工具(如Entity Framework、NHibernate等)。 ORM(Object Relation Mapping)工具,顧名思義,它的角色就是為了解決“關係”和“面向對象”之間的“失配”,它可以使得開發人員不用過多關心持久層而可以花更多的時間專注於業務。
Entity Framework(EF)是微軟以ADO.NET為基礎所發展出來的ORM解決方案,以Entity Data Model(EDM) 為主。 EF利用了抽象資料結構的方式,將每個資料庫物件轉換成應用程式中的類別物件(Entity),而資料欄位都轉換為屬性(Property),關係則轉換為結合屬性(Association),讓資料庫的E/R
模型完全的轉換成物件模型,如此讓開發人員就能用熟悉的物件導向程式語言來呼叫存取。 EF 4.0 以後支援Database First、Model First、Code First三種產生模式,Code First模式用的人比較多。
概念和理論性的東西就不多講了,我也是帶著疑問去查找網絡資源根據自己的理解半摘半歸納出來的,而且鄙人不才,很多東西想描述但都不知道怎麼組織語言。
本文目的主要是讓讀者對EF有個感性的認識,然後了解EF在ASP.NET MVC中的應用,不會去研究原理性的東西,下次有空再單獨介紹EF吧。
使用Entity Framework
接著上一篇部落格文章[ASP.NET
MVC 小牛之路]05 - 使用Ninject,我們現在要把程式碼中手工造的資料改成從資料庫讀取。為此,我們先準備下資料庫。本範例使用的是MS SQL Server,使用其他資料庫也是一樣的。先建立一個名為BookShop的資料庫,然後執行下面的腳本建立一個Books表:
CREATE TABLE Books ( [ID] INT NOT NULL PRIMARY KEY IDENTITY, [Title] NVARCHAR(100) NOT NULL, [Isbn] VARCHAR(20) NOT NULL, [Summary] NVARCHAR(1000) NOT NULL, [Author] NVARCHAR(50) NOT NULL, [Thumbnail] VARBINARY(MAX), [Price] DECIMAL(16, 2) NOT NULL, [Published] DATE NOT NULL, )
然后随便在表中加几条用于测试的数据:
接下来我们就要让应用程序连接数据库了。由于上一篇博文是我在公司用休息的时间写的,公司的电脑装的是VS2010,家里的笔记本装的是VS2012,所以得重新把上篇博文的示例移到VS2012上,对于本示例,VS2010和VS2012都是一样的。上一篇示例项目的目录结构如下:
本文的示例将在上篇的这个示例基础上继续。
用NuGet在BookShop.Domain工程中安装Entity Framework包,方法请参考本系列的上一篇文章。
在BookShop.Domain工程的Concrete文件夹中添加一个名为EFDbContext的类,代码如下:
public class EFDbContext : DbContext { public DbSet<Book> Books { get; set; } }
使用EF Code First第一步就是创建一个继承自System.Data.Entity.DbContext的类,这个类将为数据库中的每个表定义一个属性,属性的名称代表数据库中的表名。DbSet作为返回类型,它是用于生成CRUD(Create、Read、Update和Delete)操作的装置,映射数据库表的行。
我们需要在BookShop.WebUI工程中的web.config配置文件中添加数据库的连接字符串,来告诉EF怎样连接数据库。根据自己机器上的数据库配置连接字符串如下:
<connectionStrings> <add name="EFDbContext" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=BookShop;User ID=sa;Password=sa" providerName="System.Data.SqlClient" /> </connectionStrings>
接下来,我们把BookShop.Domain工程下Concrete文件中的BookRepository类文件改造一下,把代码中手工造的数据改成从数据库读取,以测试应用程序是否可以正常连接数据库。修改后的BookRepository类如下:
public class BookRepository : IBookRepository { private EFDbContext context = new EFDbContext(); public IQueryable<Book> Books { get { return context.Books; } } }
在我们的这个仓储类中,我们改使用EF,通过创建一个EFDbContext类的实例来获取数据库中的数据。如你所见,我们不需要自己写ADO.NET代码去连接和读取数据库,非常简洁明了,我们就是这样使用Entity Framework的。我们来看一下运行效果吧:
到这我们已经成功使用EF连接上了数据库,并从数据库中读取出来了数据。我们还可以通过Linq进行非常灵活的查询,就像写SQL一样。比如要查询价格在100元以下的前10条记录,并且按价格从低到高显示,那么我们可以在BookShop.WebUI工程下的BookController中的List方法中这样写:
public ViewResult List() { return View(repository.Books .OrderBy(b => b.Price) .Where(b => b.Price < 100) .Take(10)); }
或许你很快就会对EF获取数据库的方式产生这样的疑问:EF从数据库中读取整个Books表的数据到内存,然后返回给调用者(上面代码中的repository.Books)用Linq语句过滤用户想要的前10条数据,如果Books表中有几百万条数据,那内存岂不是完蛋了,EF不会这么傻吧?EF会不会根据Linq查询语句智能地生成SQL文本再到数据库中去查询数据呢?这里就要讲讲IQueryable和IEnumerable了。
IQueryable 和 IEnumerable
其实,对于上面的即有过虑又有排序的条件查询Linq语句,EF是读取数据库中整个Books表中的数据到内存,还是根据Linq查询语句智能的生成SQL再执行查询,完全编码者来决定的。我们打开BookShop.Domain工程的BookRepository类文件,请注意该类中Books属性的返回类型:
... public IQueryable<Book> Books { get { return context.Books; } }
我们对使用IQueryable作为返回类型提了个疑问:为什么用IQueryable而不用IEnumerable作为返回类型?答案是:使用IQueryable,EF会根据调用者的Linq表达式先生成相应的SQL查询语句,然后到数据库中执行查询,查询出来的数据即是用户想要的数据;而使用IEnumerable,Linq表达式的过滤、排序等操作都是在内存中发生的,即EF会先从数据库中把整个表的数据查询出来放在内存中,然后由调用者使用Linq语句进行过滤、排序等操作。是不是这样呢?我们来监视一下两种情况EF生成的SQL语句就知道了。
我们先来看看使用IQueryable的情况。重新运行一下程序,然后使用SQL Server Management Studio的活动和监视器查看一下我们的BookShop应用程序所执行的SQL语句,结果如下:
结果证明使用IQueryable,EF是先根据Linq表达式生成相应的SQL语句再执行查询的。
我们再稍稍修改一下代码来看看用IEnumerable的情况。把BookRepository类修改如下:
public class BookRepository : IBookRepository { private EFDbContext context = new EFDbContext(); public IEnumerable<Book> Books { get { return context.Books; } } }
当然BookRepository类所实现的IBookRepository接口(在BookShop.Domain工程的Abstract文件夹中)也要改一下:
public interface IBookRepository { IEnumerable<Book> Books { get; } }
再重新运行一下应用程序,用活动和监视器查看最后执行的SQL语句如下图:
我们看到改用IEnumerable后,EF生成的SQL没有任何过滤、排序等的操作,它一次把表中的所有数据都Select出来,和上面写的Linq表达式一点都没关系。
IQueryable虽然可以很智能地根据Linq表达式生成相应的SQL语句,但毕竟有一个分析Linq表达式的过程,相对来说性能比IEnumerable要差。那么我们什么时候用IEnumerable,什么时候用IQueryable呢?我想,对于少量的数据(比如从数据库中读取应用程序相关的系统信息)和不需要对数据进行过滤操作的情况,用IEnumerable比较适合;对于数据量较大需要对数据进行过滤(比如分页查询)的情况,则用IQueryable比较合适。
以上就是[ASP.NET MVC 小牛之路]06 - 使用 Entity Framework的内容,更多相关内容请关注PHP中文网(www.php.cn)!