在Entity Framework 4.0中使用 Repository 和 Unit of Work 模式
【原文地址】Using Repository and Unit of Work patterns with Entity Framework 4.0?
【原文發(fā)表日期】 16 June 09 04:08 PM
如果你一直在關(guān)注這個博客的話,你知道我最近在討論我們加到Entity Framework 4.0中的POCO功能的方方面面,新加的POCO支持促成了在Entity Framework中實現(xiàn)透明性持久的新方式,而該方式在Entity Framework 3.5中是無法實現(xiàn)的。
如果你錯過了我的POCO系列,為方便起見,我在下面列了出來,快速瀏覽一下也許是個不錯的主意。
POCO in Entity Framework : Part 1 – The Experience(【翻譯】實體框架中的POCO支持 - 第一部分 - 體驗?)
POCO in Entity Framework : Part 2 – Complex Types, Deferred Loading and Explicit Loading?(【翻譯】實體框架中的POCO支持 - 第二部分 - 復(fù)雜類型,延遲裝載和顯式裝載?)
POCO in Entity Framework : Part 3 – Change Tracking with POCO?(【翻譯】實體框架中的POCO支持 - 第三部分 - POCO的變動跟蹤)
在這個貼子里,我想要看一下如何將我們的例子做進一步擴充,使用一些諸如Repository?和?Unit Of Work這樣常見的模式,這樣我們可以在我們的例子中實現(xiàn)一些特定于持久性的關(guān)注。
將我們基于Northwind的例子做一下擴充,譬如說,我有興趣支持下列與Customer(客戶)實體相關(guān)的操作:
- 通過ID來查詢客戶
- 通過名字來查詢客戶
- 往數(shù)據(jù)庫中加一個新客戶
我還想能夠基于ID來查詢產(chǎn)品。
最后,給定一個客戶,我想要能夠往數(shù)據(jù)庫中加一個Order(訂單)。
在進入細(xì)節(jié)之前,我想先說明兩件事情:
- 處理這個問題,“正確”的方式不止一個。在過程中,我會做許多簡化的假設(shè),其目的是展示一幅非常高層次的草圖,示范如何實現(xiàn)這2個模式來解決手上的問題。
- 通常來說,在使用TDD時,我會從測試開始,使用我的測試來逐步展開我的設(shè)計。因為在這個例子中我沒有按TDD做,請耐心點,如果你看到我在做象預(yù)先定義接口這樣的事情,而不是讓測試和共用的東西來主宰接口的必要性,等等。
實現(xiàn) Repository 模式
讓我們先開始實現(xiàn)針對Customer實體的操作,看一下Customer的repository的樣子:
public interface ICustomerRepository { Customer GetCustomerById(string id);IEnumerable<Customer> FindByName(string name);void AddCustomer(Customer customer); }?
這個repository接口看上去滿足有關(guān)Customer的所有需求:
- GetCustomerById?應(yīng)該允許我通過主鍵來得單個客戶實體
- FindByName?應(yīng)該允許我查詢客戶
- AddCustomer?應(yīng)該允許我往數(shù)據(jù)庫中加一個客戶
這暫時聽上去不錯,象這樣,為你的repository定義一個接口是個好主意,特別是你對使用mocks(模擬對象) 或 fakes (假冒對象)來編寫測試感興趣的話,接口允許你完全不用考慮數(shù)據(jù)庫,促成更好的單元測試。在將來會有更多的博客貼子討論可測試性,mocks 和 fakes,等等。
你也許可以進一步擴展這個接口定義,定義一個共用的IRepository,來處理多個repository類型中共有的關(guān)注。如果對你有用的話,這是件值得做的事。在這個特定的例子中,我沒看到其必要性,所以我就免了。但在你添加更多的repository以及重構(gòu)時,這完全有可能會變得非常重要。
讓我們拿這個repository來看一下如何利用Entity Framework來做一個實現(xiàn),允許數(shù)據(jù)訪問:
首先,我需要一個可以用來查詢數(shù)據(jù)的ObjectContext。你也許想作為repository構(gòu)造器的一部分生成一個ObjectContext,但最好還是把那個關(guān)注從repository中去除,在別的地方處理為好。
這是我的構(gòu)造器:
public CustomerRepository(NorthwindContext context) {if (context == null)throw new ArgumentNullException("context");_context = context; }?
在上面的代碼片段中,NorthwindContext是個我自己的ObjectContext類型。
讓我們來提供?ICustomerRepository?接口所需的方法的實現(xiàn)。
GetCustomerById?非常容易實現(xiàn),多虧了LINQ。使用標(biāo)準(zhǔn)的LINQ運算符,我們可以象這樣實現(xiàn)?GetCustomerById?:
public Customer GetCustomerById(string id) {return _context.Customers.Where(c => c.CustomerID == id).Single(); }?
類似地,FindByName?可以象這樣。 再一次,LINQ支持使得其實現(xiàn)非常容易:
public IEnumerable<Customer> FindByName(string name) {return _context.Customers.Where( c => c.ContactName.StartsWith(name)).ToList(); }?
注意,我選擇將結(jié)果呈示為IEnumerable<T>,你也許會選擇將這呈示為IQueryable<T>。這么做有其含意,在這個情形下,我對呈示建立在repository返回結(jié)果之上的基于IQueryable的另外的查詢組合,不是很感興趣。
最后,讓我們來看一下可以如何實現(xiàn)AddCustomer:
public void AddCustomer(Customer customer) {_context.Customers.AddObject(customer); }?
你也許想把保存功能作為AddCustomer方法的一部分來實現(xiàn)。那么做也許對這個簡單的例子沒問題,但一般來說,這不是個好主意,這正是Unit of Work出場的地方,過一會兒,我們會看到如何使用這個模式來允許我們實現(xiàn)和協(xié)調(diào)Save行為。
這里是使用Entity Framework來處理持久性的CustomerRepository的完整實現(xiàn):
public class CustomerRepository : ICustomerRepository {private NorthwindContext _context;public CustomerRepository(NorthwindContext context){if (context == null)throw new ArgumentNullException("context");_context = context;}public Customer GetCustomerById(string id){return _context.Customers.Where(c => c.CustomerID == id).Single();}public IEnumerable<Customer> FindByName(string name){return _context.Customers.Where(c => c.ContactName.StartsWith(name)).AsEnumerable<Customer>(); }public void AddCustomer(Customercustomer){_context.Customers.AddObject(customer);} }?
這里是我們可以如何在客戶端代碼中使用該repository:
CustomerRepository repository = new CustomerRepository(context); Customer c = new Customer( ... ); repository.AddCustomer(c); context.SaveChanges();?
在處理與Product和Order相關(guān)的需求時,我可以定義下列接口(建造類似CustomerRepository一樣的實現(xiàn))。為簡便起見,我把其細(xì)節(jié)省略了。
public interface IProductRepository {Product GetProductById(int id); }public interface IOrderRepository {void AddOrder(Order order); }?
使用ObjectContext實現(xiàn) Unit of Work (工作單元) 模式
你也許已經(jīng)注意到了,盡管我們沒有實現(xiàn)任何特定的模式來明確地地允許我們將相關(guān)操作聚合進一個工作單元(unit of work),但我們已經(jīng)通過NorthwindContext(我們的ObjectContext類)免費獲取了Unit of Work功能。
其想法是,我可以使用Unit of Work將一套相關(guān)的操作聚合在一起,Unit of Work記錄我感興趣的變動,直到我準(zhǔn)備將它們保存到數(shù)據(jù)庫為止。最終,當(dāng)我準(zhǔn)備保存時,我可以那么做。
我可以象這樣定義一個“Unit of Work”接口:
public interface IUnitOfWork { void Save(); }?
注意,在Unit of Work中,你也許還會選擇實現(xiàn)Undo / Rollback功能。在使用Entity Framework時,推薦的undo做法是,丟棄你的上下文以及你想undo的變動。
我已經(jīng)提到,我們的ObjectContext類型(NorthwindContext)在極大程度上支持Unit of Work模式。 基于我剛定義的契約,為了使得事情更為明確一點,我可以將NorthwindContext類改成實現(xiàn)IUnitOfWork接口:
public class NorthwindContext : ObjectContext, IUnitOfWork { public void Save(){SaveChanges();}. . . }?
在做了這個變動之后,我需要對我們的repository實現(xiàn)做個小的調(diào)整:
public class CustomerRepository : ICustomerRepository {private NorthwindContext _context;public CustomerRepository(IUnitOfWork unitOfWork){if (unitOfWork == null)throw new ArgumentNullException("unitOfWork");_context = unitOfWork as NorthwindContext;}public Customer GetCustomerById(string id){return _context.Customers.Where(c => c.CustomerID == id).Single();}public IEnumerable<Customer> FindByName(string name){return _context.Customers.Where(c => c.ContactName.StartsWith(name)).AsEnumerable<Customer>();}public void AddCustomer(Customer customer){_context.Customers.AddObject(customer);} }?
?
就這么簡單!現(xiàn)在,我們有了一個對IUnitOfWork友好的repository,你甚至可以使用基于IUnitOfWork的上下文來協(xié)調(diào)跨越多個repository的工作。下面是一個將訂單加到數(shù)據(jù)庫的例子,需要多個repository的工作來查詢數(shù)據(jù),最終將記錄保存回數(shù)據(jù)庫中去:
IUnitOfWork unitOfWork = new NorthwindContext();CustomerRepository customerRepository = new CustomerRepository(unitOfWork); Customer customer = customerRepository.GetCustomerById("ALFKI");ProductRepository productRepository = new ProductRepository(unitOfWork); Product product = productRepository.GetById(1);OrderRepository orderRepository = new OrderRepository(unitOfWork);Order order = new Order(customer); order.AddNewOrderDetail(product, 1);orderRepository.AddOrder(order);unitOfWork.Save();?
非常有趣地看到,為了在Entity Framework之上使用Repository 和 Unit of Work模式來訪問數(shù)據(jù),我們要編寫的代碼是如此地少。Entity Framework的LINQ支持以及原裝的Unit of Work功能使得在Entity Framework之上建立repository簡單之極。
另一個要注意的事情是,我在這個貼子里討論的很多東西,都不是與Entity Framework 4.0特別有關(guān),所有這些一般原理在Entity Framework 3.5下也適用。但要能夠象上面展示的那樣,在POCO支持的基礎(chǔ)之上,使用Repository?和Unit of Work,確實顯示了Entity Framework 4.0中的威力。我希望你會發(fā)現(xiàn)這非常有用。
最后,我要重申一下,有很多方式可以處理這個題目,在應(yīng)用Entity Framework, Repository 和 Unit of Work時,還有許多變種方案可以符合你的需求。你也許會選擇實現(xiàn)公共的IRepository接口,你也許還會選擇實現(xiàn)Repository<TEntity>基類,來給予你一些跨越所有repository的常用repository功能。
嘗試一些方法,看哪個最適用于你。我在這里討論的只是一般的指南而已,但我希望這足夠讓你起步。更重要的是,我希望這示范了如何可以使用我們在Entity Framework 4.0中引進的POCO支持,來幫助你建造Entity Framework友好的領(lǐng)域模型,而不必違背透明持久性方面的基本原則。
包含我上面一些示范代碼的項目附在本貼之后。在我們的將來貼子中討論單元測試,TDD和可測試性時,我們還將對這個題目做更多的討論,敬請期待。
轉(zhuǎn)載于:https://www.cnblogs.com/colder/p/4194846.html
總結(jié)
以上是生活随笔為你收集整理的在Entity Framework 4.0中使用 Repository 和 Unit of Work 模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL server 复习一
- 下一篇: [Everyday Mathematic