在上一次的教程中,你已經使用繼承來消除在 Student 和 Instructor 實體之間的重復代碼。在這個教程中,你將要看到使用倉儲和工作單元模式進行增、刪、改、查的一些方法。像前面的教程一樣,你將要修改已經創建的頁面中代碼的工作方式,而不是新創建的頁面。
9-1 倉儲和工作單元模式
倉儲和工作單元模式用來在數據訪問層和業務邏輯層之間創建抽象層。實現這些模式有助於隔離數據存儲的變化,便於自動化的單元測試或者測試驅動的開發 ( TDD )。 在這個教程中,你將要為每個實體類型實現一個倉儲類。對於 Student 實體來說,你需要創建一個倉儲接口和一個倉儲類。當在控制器中實例化倉儲對象的時候。你將會通過接口來使用它,當控制器在 Web 服務器上運行的時候,控制器將會接受任何實現倉儲接口的對象引用。通過接收倉儲對象進行數據的存儲管理,使得你可以容易地控制測試,就像使用內存中的集合一樣。 在教程的最後,你將要在 Course 控制器中對 Course 和 Department 實體使用多個倉儲和一個工作單元類。工作單元類則通過創建一個所有倉儲共享的數據庫上下文對象,來組織多個倉儲對象。如果你希望執行自動化的單元測試,你也應該對 Student類通過相同的方式創建和使用接口。不管怎樣,為了保持教程的簡單,你將不會通過接口創建和使用這些類。 下面的截圖展示了在控制器和上下文之間的概念圖,用來比較與不使用倉儲或工作單元模式的區別。
在這個教程中不會創建單元測試,在 MVC 應用中使用倉儲模式進行 TDD 的相關信息,可以查看 MSDN 網站中的 Walkthrough: Using TDD with ASP.NET MVC ,EF 團隊博客中的 Using Repository and Unit of Work patterns with Entity Framework 4.0 ,以及 Julie Lerman 的博客 Agile Entity Framework 4 Repository 系列。 注意:有多種方式可以實現倉儲和工作單元模式。配合工作單元類可以使用也可以不使用倉儲類。可以對所有的實體類型實現一個簡單的倉儲,或者每種類型一個。如果為每種類型實現一個倉儲,還可以通過分離的類,或者泛型的基類然後派生,或者抽象基類然後派生。可以將業務邏輯包含在倉儲中,或者限制只有數據訪問邏輯。也可以通過在實體中使用 IDbSet 接口代替 DbSet 類為數據庫上下文類創建一個抽象層。在這個教程中展示的目標實現了抽象層,只是其中一種考慮,並不是針對所有的場景和環境都適用。
9-2 創建 Student 倉儲類
在 DAL 文件夾中,創建一個文件名為 IStudentRepository.cs 的文件,將當前的代碼使用如下代碼替換。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using ContosoUniversity.Models; namespace ContosoUniversity.DAL { public interface IStudentRepository : IDisposable { IEnumerable<Student> GetStudents(); Student GetStudentByID(int studentId); void InsertStudent(Student student); void DeleteStudent(int studentID); void UpdateStudent(Student student); void Save(); } }
代碼定義了一套典型的增、刪、改、查方法。包括兩個讀取方法 – 一個返回所有的學生實體,一個通過 ID 查詢單個實體。 在 DAL 文件夾中,創建名為 StudentRepository.cs 的類文件,使用下面的代碼替換原有的代碼,這個類實現了 IStudentRepository 接口。
using System; using System.Collections.Generic; using System.Linq; using System.Data; using ContosoUniversity.Models; namespace ContosoUniversity.DAL { public class StudentRepository : IStudentRepository, IDisposable { private SchoolContext context; public StudentRepository(SchoolContext context) { this.context = context; } public IEnumerable<Student> GetStudents() { return context.Students.ToList(); } public Student GetStudentByID(int id) { return context.Students.Find(id); } public void InsertStudent(Student student) { context.Students.Add(student); } public void DeleteStudent(int studentID) { Student student = context.Students.Find(studentID); context.Students.Remove(student); } public void UpdateStudent(Student student) { context.Entry(student).State = EntityState.Modified; } public void Save() { context.SaveChanges(); } private bool disposed = false; protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { context.Dispose(); } } this.disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } }