標籤:
原文連結:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pattern-and-uni/
系列目錄:
- Relationship in Entity Framework Using Code First Approach With Fluent API【【使用EF Code-First方式和Fluent API來探討EF中的關係】】
- Code First Migrations with Entity Framework【使用EF 做資料庫遷移】
- CRUD Operations Using Entity Framework 5.0 Code First Approach in MVC【在MVC中使用EF 5.0做增刪查改】
- CRUD Operations Using the Repository Pattern in MVC【在MVC中使用倉儲模式,來做增刪查改】
- CRUD Operations Using the Generic Repository Pattern and Unit of Work in MVC【在MVC中使用泛型倉儲模式和工作單元來做增刪查改】
- CRUD Operations Using the Generic Repository Pattern and Dependency Injection in MVC【在MVC中使用泛型倉儲模式和依賴注入,來做增刪查改】
這篇文章,我將會介紹在ASP.NET MVC應用程式中使用泛型倉儲模式和工作單元。我將開發一個程式,對Book實體進行增刪查改,為了保證這篇文章簡單,便於大家理解泛型倉儲模式和工作單元,在這篇文章中,我將只會使用一個Book實體。
在上篇文章中“4.CRUD Operations Using the Repository Pattern in MVC【在MVC中使用倉儲模式進行增刪查改】“講到了倉儲模式,裡面我們為Book實體,建立了一個倉儲類,但是這個倉儲僅僅是只能為一個實體服務的。試想一下,如果是真正的企業級開發,我們會有很多實體,難道,我們要為每一個實體都建立一個倉儲類嗎???顯然是不現實的。對於這個問題,我們需要建立一個可以為所有實體公用的倉儲,所以這裡引入泛型倉儲。這樣就避免了重複編碼。
下面的圖中,講到了兩個開發人員,討論一個問題:"是否需要建立一個新的零件或者是利用已經存在的零件?" ------如果你選擇第一種,那麼就是這篇文章講到的,"4.CRUD Operations Using the Repository Pattern in MVC【在MVC中使用倉儲模式進行增刪查改】"
但我在這裡選擇第二種,也就是這篇文章,我將要講到的。--使用泛型倉儲模式來重用代碼,減少冗餘代碼。
既然說到倉儲,那麼什麼是倉儲模式呢?
倉儲模式旨在資料訪問層和商務邏輯層之間建立一個抽象層。倉儲模式是資料訪問模式,旨在達到資料訪問的更鬆散的耦合性。我們在單獨的類,或者類庫中建立資料訪問的邏輯,這就是倉儲。倉儲的職責就是和商務邏輯層進行通訊。
在這篇文章中,我將會為所有的實體設計一個公用的泛型倉儲,另外還有一個工作單元類。這個工作單元類,為每個實體建立倉儲的執行個體,然後倉儲執行個體用來做增刪查改操作。我在控制器中建立工作單元類的執行個體,然後依據具體的實體,建立倉儲的執行個體,然後就可以使用倉儲中的方法進行每個操作了。
下面的圖表顯示了倉儲和EF資料內容之間的關係。在圖中,MVC控制器直接通過工作單元和倉儲進行互動,而不是直接和EF進行互動。
講到這裡,大家可能就會有疑問了,為什麼要使用工作單元呢???
工作單元就像它的名字一樣,做某件事情。在這篇文章中,工作單元主要做的是:我們建立工作單元的執行個體,然後工作單元為我們初始化EF資料內容,然後每個倉儲的執行個體都使用同一個資料內容執行個體進行資料庫操作。因此工作單元就是,用來確保所有的倉儲執行個體都使用同一個資料內容執行個體。
好了理論到此為止,講的差不多了。現在我們開始進入正題:
先看看項目的結構:
這三個項目就不用做過多介紹了吧,前面的文章已經說了很多次了...
EF.Entity類庫中,添加BaseEntity實體:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace EF.Entity{ public abstract class BaseEntity { /// <summary> /// ID編號 /// </summary> public int ID { get; set; } /// <summary> /// 添加時間 /// </summary> public DateTime AddedDate { get; set; } /// <summary> /// 修改時間 /// </summary> public DateTime ModifiedDate { get; set; } /// <summary> /// IP地址 /// </summary> public string IP { get; set; } }}
Book實體:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace EF.Entity{ public class Book:BaseEntity { /// <summary> /// 書名 /// </summary> public string Title { get; set; } /// <summary> /// 作者 /// </summary> public string Author { get; set; } /// <summary> /// ISBN編號 /// </summary> public string ISBN { get; set; } /// <summary> /// 出版時間 /// </summary> public DateTime PublishedDate { get; set; } }}
Entity.Data類庫中BookMap類:
using EF.Entity;using System;using System.Collections.Generic;using System.ComponentModel.DataAnnotations.Schema;using System.Data.Entity.ModelConfiguration;using System.Linq;using System.Text;using System.Threading.Tasks;namespace EF.Data{ public class BookMap:EntityTypeConfiguration<Book> { public BookMap() { //配置主鍵 this.HasKey(s => s.ID); //配置欄位 this.Property(s => s.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); this.Property(s => s.Author).HasColumnType("nvarchar").HasMaxLength(50).IsRequired(); this.Property(s => s.AddedDate).IsRequired(); this.Property(s => s.IP).IsOptional(); this.Property(s => s.ISBN).HasColumnType("nvarchar").HasMaxLength(50).IsRequired(); this.Property(s => s.ModifiedDate).IsOptional(); this.Property(s => s.PublishedDate).IsRequired(); this.Property(s => s.Title).HasColumnType("nvarchar").HasMaxLength(50).IsRequired(); //配置表名 this.ToTable("Books"); } }}
EF資料內容類:
using System;using System.Collections.Generic;using System.Data.Entity;using System.Data.Entity.ModelConfiguration;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;namespace EF.Data{ public class EFDbContext:DbContext { public EFDbContext() : base("name=DbConnectionString") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { var typesToRegister = Assembly.GetExecutingAssembly().GetTypes() .Where(type => !String.IsNullOrEmpty(type.Namespace)) .Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)); foreach (var type in typesToRegister) { dynamic configurationInstance = Activator.CreateInstance(type); modelBuilder.Configurations.Add(configurationInstance); } //base.OnModelCreating(modelBuilder); } }}
然後啟用資料庫遷移,自動產生資料庫,這裡的步驟就省略了,因為前面的文章已經說了。
接著,在EF.Data項目中建立泛型倉儲類,裡面提供了增刪查改的方法,這個泛型的倉儲類中,有一個帶DbContext參數的建構函式,所以當我們執行個體化倉儲的時候,傳遞一個資料內容對象給倉儲,所有的實體就可以使用同一個資料內容對象了。我們使用了資料內容的SaveChange方法,但是你同樣可以使用工作單元類的Save方法,因為這兩者使用的是同一個資料內容對象,下面是泛型的倉儲類代碼:【為了使文章更容易理解,這裡我不建立泛型的倉儲介面】。
View Code
下面建立工作單元類,UnitOfWork,這個工作單元類繼承自IDisposable介面,所以它的執行個體將會在每個控制器中被 釋放掉。工作單元類初始化了程式的上下文,工作單元類的核心就是Repository<T>() 類型的泛型方法,這個方法為繼承自BaseEntity的每個實體返回了倉儲執行個體。下面是工作單元類的代碼:
using EF.Entity;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace EF.Data{ public class UnitOfWork : IDisposable { private readonly EFDbContext db; private bool disposed; private Dictionary<string, object> repositories; public UnitOfWork(EFDbContext context) { this.db = context; //建構函式中初始化內容物件 } public UnitOfWork() { db = new EFDbContext(); //建構函式中初始化內容物件 } #region Dispose public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public virtual void Dispose(bool disposing) { if (!disposed) { if (disposing) { db.Dispose(); } } disposed = true; } #endregion #region Save public void Save() { db.SaveChanges(); } #endregion #region Repository<T>() public Repository<T> Repository<T>() where T : BaseEntity { if (repositories == null) { repositories = new Dictionary<string, object>(); } var type = typeof(T).Name;//擷取當前成員名稱 if (!repositories.ContainsKey(type))//如果repositories中不包含Name { var repositoryType = typeof(Repository<>);//擷取Repository<>類型 var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), db); repositories.Add(type, repositoryInstance); } return (Repository<T>)repositories[type]; } #endregion }}
好了,底層的代碼,寫完了,現在開始寫控制器的代碼:我們建立一個Book控制器,進行增刪查改。
View Code
Index 視圖代碼:
@model IEnumerable<EF.Entity.Book><div class="book-example panel panel-primary"> <div class="panel-heading panel-head">Books Listing</div> <div class="panel-body"> <a id="createEditBookModal" href="@Url.Action("CreateEditBook")" class="btn btn-success"> <span class="glyphicon glyphicon-plus"></span>Book </a> <table class="table" style="margin: 4px"> <tr> <th> @Html.DisplayNameFor(model => model.Title) </th> <th> @Html.DisplayNameFor(model => model.Author) </th> <th> @Html.DisplayNameFor(model => model.ISBN) </th> <th> Action </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.Author) </td> <td> @Html.DisplayFor(modelItem => item.ISBN) </td> <td> @Html.ActionLink("Edit", "CreateEditBook", new { id = item.ID }, new { @class = "btn btn-success" }) | @Html.ActionLink("Details", "DetailBook", new { id = item.ID }, new { @class = "btn btn-primary" }) | @Html.ActionLink("Delete", "DeleteBook", new { id = item.ID }, new { @class = "btn btn-danger" }) </td> </tr> } </table> </div></div>
CraeteEdit視圖代碼:
@model EF.Entity.Book@{ ViewBag.Title = "Create Edit Book";}<div class="book-example panel panel-primary"> <div class="panel-heading panel-head">Add / Edit Book</div> <div class="panel-body"> @using (Html.BeginForm()) { <div class="form-horizontal"> <div class="form-group"> @Html.LabelFor(model => model.Title, new { @class = "col-lg-1 control-label" }) <div class="col-lg-9"> @Html.TextBoxFor(model => model.Title, new { @class = "form-control" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.ISBN, new { @class = "col-lg-1 control-label" }) <div class="col-lg-9"> @Html.TextBoxFor(model => model.ISBN, new { @class = "form-control" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Author, new { @class = "col-lg-1 control-label" }) <div class="col-lg-9"> @Html.TextBoxFor(model => model.Author, new { @class = "form-control" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Published, new { @class = "col-lg-1 control-label" }) <div class="col-lg-9"> @Html.TextBoxFor(model => model.Published, new { @class = "form-control datepicker" }) </div> </div> <div class="form-group"> <div class="col-lg-8"></div> <div class="col-lg-3"> @Html.ActionLink("Back to List", "Index", null, new { @class = "btn btn-default" }) <button class="btn btn-success" id="btnSubmit" type="submit"> Submit </button> </div> </div> </div> } </div></div>@section scripts 這裡的話,在布局頁中要添加這個塊: @RenderSection("scripts", required: false){ <script src="~/Scripts/bootstrap-datepicker.js" type="text/javascript"></script> <script src="~/Scripts/book-create-edit.js" type="text/javascript"></script>}
DeleteBook視圖:
View Code
Detail視圖:
View Code
修改一下預設路由為Book控制器,Index方法,然後運行項目》》》
5.在MVC中使用泛型倉儲模式和工作單元來進行增刪查改