系列目錄:

 

   

源代碼下載:https://github.com/caofangsheng93/GenericRepositoryCURD

  這篇文章,我將會(huì)介紹在ASP.NET MVC應(yīng)用程序中使用泛型倉(cāng)儲(chǔ)模式和工作單元。我將開(kāi)發(fā)一個(gè)程序,對(duì)Book實(shí)體進(jìn)行增刪查改,為了保證這篇文章簡(jiǎn)單,便于大家理解泛型倉(cāng)儲(chǔ)模式和工作單元,在這篇文章中,我將只會(huì)使用一個(gè)Book實(shí)體。

在上篇文章中“4.CRUD Operations Using the Repository Pattern in MVC【在MVC中使用倉(cāng)儲(chǔ)模式進(jìn)行增刪查改】“講到了倉(cāng)儲(chǔ)模式,里面我們?yōu)锽ook實(shí)體,創(chuàng)建了一個(gè)倉(cāng)儲(chǔ)類(lèi),但是這個(gè)倉(cāng)儲(chǔ)僅僅是只能為一個(gè)實(shí)體服務(wù)的。試想一下,如果是真正的企業(yè)級(jí)開(kāi)發(fā),我們會(huì)有很多實(shí)體,難道,我們要為每一個(gè)實(shí)體都創(chuàng)建一個(gè)倉(cāng)儲(chǔ)類(lèi)么???顯然是不現(xiàn)實(shí)的。對(duì)于這個(gè)問(wèn)題,我們需要?jiǎng)?chuàng)建一個(gè)可以為所有實(shí)體公用的倉(cāng)儲(chǔ),所以這里引入泛型倉(cāng)儲(chǔ)。這樣就避免了重復(fù)編碼。

下面的圖中,講到了兩個(gè)開(kāi)發(fā)者,討論一個(gè)問(wèn)題:"是否需要?jiǎng)?chuàng)建一個(gè)新的零件或者是利用已經(jīng)存在的零件?"  ------如果你選擇第一種,那么就是這篇文章講到的,"4.CRUD Operations Using the Repository Pattern in MVC【在MVC中使用倉(cāng)儲(chǔ)模式進(jìn)行增刪查改】"

但我在這里選擇第二種,也就是這篇文章,我將要講到的。--使用泛型倉(cāng)儲(chǔ)模式來(lái)重用代碼,減少冗余代碼。

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

 

既然說(shuō)到倉(cāng)儲(chǔ),那么什么是倉(cāng)儲(chǔ)模式呢?

倉(cāng)儲(chǔ)模式旨在數(shù)據(jù)訪問(wèn)層和業(yè)務(wù)邏輯層之間創(chuàng)建一個(gè)抽象層。倉(cāng)儲(chǔ)模式是數(shù)據(jù)訪問(wèn)模式,旨在達(dá)到數(shù)據(jù)訪問(wèn)的更松散的耦合性。我們?cè)趩为?dú)的類(lèi),或者類(lèi)庫(kù)中創(chuàng)建數(shù)據(jù)訪問(wèn)的邏輯,這就是倉(cāng)儲(chǔ)。倉(cāng)儲(chǔ)的職責(zé)就是和業(yè)務(wù)邏輯層進(jìn)行通信。

在這篇文章中,我將會(huì)為所有的實(shí)體設(shè)計(jì)一個(gè)公共的泛型倉(cāng)儲(chǔ),另外還有一個(gè)工作單元類(lèi)。這個(gè)工作單元類(lèi),為每個(gè)實(shí)體創(chuàng)建倉(cāng)儲(chǔ)的實(shí)例,然后倉(cāng)儲(chǔ)實(shí)例用來(lái)做增刪查改操作。我在控制器中創(chuàng)建工作單元類(lèi)的實(shí)例,然后依據(jù)具體的實(shí)體,創(chuàng)建倉(cāng)儲(chǔ)的實(shí)例,然后就可以使用倉(cāng)儲(chǔ)中的方法進(jìn)行每個(gè)操作了。

下面的圖表顯示了倉(cāng)儲(chǔ)和EF數(shù)據(jù)上下文之間的關(guān)系。在圖中,MVC控制器直接通過(guò)工作單元和倉(cāng)儲(chǔ)進(jìn)行交互,而不是直接和EF進(jìn)行交互。

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

 

 

講到這里,大家可能就會(huì)有疑問(wèn)了,為什么要使用工作單元呢???

工作單元就像它的名字一樣,做某件事情。在這篇文章中,工作單元主要做的是:我們創(chuàng)建工作單元的實(shí)例,然后工作單元為我們初始化EF數(shù)據(jù)上下文,然后每個(gè)倉(cāng)儲(chǔ)的實(shí)例都使用同一個(gè)數(shù)據(jù)上下文實(shí)例進(jìn)行數(shù)據(jù)庫(kù)操作。因此工作單元就是,用來(lái)確保所有的倉(cāng)儲(chǔ)實(shí)例都使用同一個(gè)數(shù)據(jù)上下文實(shí)例。

 

好了理論到此為止,講的差不多了?,F(xiàn)在我們開(kāi)始進(jìn)入正題:

 

先看看項(xiàng)目的結(jié)構(gòu):

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

 

這三個(gè)項(xiàng)目就不用做過(guò)多介紹了吧,前面的文章已經(jīng)說(shuō)了很多次了...

 

EF.Entity類(lèi)庫(kù)中,添加BaseEntity實(shí)體:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

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編號(hào)        /// </summary>
        public int ID { get; set; }        /// <summary>
        /// 添加時(shí)間        /// </summary>
        public DateTime AddedDate { get; set; }        /// <summary>
        /// 修改時(shí)間        /// </summary>
        public DateTime ModifiedDate { get; set; }        /// <summary>
        /// IP地址        /// </summary>
        public string IP { get; set; }


    }
}

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

Book實(shí)體:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace EF.Entity
{   public class Book:BaseEntity
    {       /// <summary>
       /// 書(shū)名       /// </summary>
       public string Title { get; set; }       /// <summary>
       /// 作者       /// </summary>
       public string Author { get; set; }       /// <summary>
       /// ISBN編號(hào)       /// </summary>
       public string ISBN { get; set; }       /// <summary>
       /// 出版時(shí)間       /// </summary>
       public DateTime PublishedDate { get; set; }
    }
}

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

Entity.Data類(lèi)庫(kù)中BookMap類(lèi):

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

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");
        }
    }
}

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

EF數(shù)據(jù)上下文類(lèi):

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

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);       }
    }
}

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

 

然后啟用數(shù)據(jù)庫(kù)遷移,自動(dòng)生成數(shù)據(jù)庫(kù),這里的步驟就省略了,因?yàn)榍懊娴奈恼乱呀?jīng)說(shuō)了。

接著,在EF.Data項(xiàng)目中創(chuàng)建泛型倉(cāng)儲(chǔ)類(lèi),里面提供了增刪查改的方法,這個(gè)泛型的倉(cāng)儲(chǔ)類(lèi)中,有一個(gè)帶DbContext參數(shù)的構(gòu)造函數(shù),所以當(dāng)我們實(shí)例化倉(cāng)儲(chǔ)的時(shí)候,傳遞一個(gè)數(shù)據(jù)上下文對(duì)象給倉(cāng)儲(chǔ),所有的實(shí)體就可以使用同一個(gè)數(shù)據(jù)上下文對(duì)象了。我們使用了數(shù)據(jù)上下文的SaveChange方法,但是你同樣可以使用工作單元類(lèi)的Save方法,因?yàn)檫@兩者使用的是同一個(gè)數(shù)據(jù)上下文對(duì)象,下面是泛型的倉(cāng)儲(chǔ)類(lèi)代碼:【為了使文章更容易理解,這里我不創(chuàng)建泛型的倉(cāng)儲(chǔ)接口】。

 

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) View Code

 

 

下面創(chuàng)建工作單元類(lèi),UnitOfWork,這個(gè)工作單元類(lèi)繼承自IDisposable接口,所以它的實(shí)例將會(huì)在每個(gè)控制器中被 釋放掉。工作單元類(lèi)初始化了程序的上下文,工作單元類(lèi)的核心就是Repository<T>() 類(lèi)型的泛型方法,這個(gè)方法為繼承自BaseEntity的每個(gè)實(shí)體返回了倉(cāng)儲(chǔ)實(shí)例。下面是工作單元類(lèi)的代碼:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

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;  //構(gòu)造函數(shù)中初始化上下文對(duì)象        }        public UnitOfWork()
        {
            db = new EFDbContext(); //構(gòu)造函數(shù)中初始化上下文對(duì)象        }        #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;//獲取當(dāng)前成員名稱(chēng)
            if (!repositories.ContainsKey(type))//如果repositories中不包含Name            {                var repositoryType = typeof(Repository<>);//獲取Repository<>類(lèi)型
                var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), db);
                repositories.Add(type, repositoryInstance);

            }            return (Repository<T>)repositories[type];

        } 
        #endregion


    }
}

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

 

 

 好了,底層的代碼,寫(xiě)完了,現(xiàn)在開(kāi)始寫(xiě)控制器的代碼:我們創(chuàng)建一個(gè)Book控制器,進(jìn)行增刪查改。

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) View Code

 

Index 視圖代碼:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

@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>

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

CraeteEdit視圖代碼:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

@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  這里的話,在布局頁(yè)中要添加這個(gè)塊: @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>}

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

DeleteBook視圖:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) View Code

Detail視圖:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) View Code

修改一下默認(rèn)路由為Book控制器,Index方法,然后運(yùn)行項(xiàng)目》》》

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

 后記:

用到的兩個(gè)js文件

book-create-edit.js

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) View Code

bootstrap-datepicker.js

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) View Code

 

 總結(jié):使用了標(biāo)準(zhǔn)清理模式,每次操作之后,工作單元對(duì)象都被銷(xiāo)毀了,再次進(jìn)行其他操作的時(shí)候,又會(huì)重新創(chuàng)建對(duì)象的實(shí)例。

完成之后項(xiàng)目的結(jié)構(gòu)是:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

 

每天學(xué)一點(diǎn),每天積累一天,進(jìn)步就不止一點(diǎn)點(diǎn)!PS:好記性不如爛筆頭,學(xué)會(huì)總結(jié),學(xué)會(huì)思考~~~ ----要飛翔,必須靠自己!