1. 程式人生 > >在MVC程式中,使用泛型倉儲模式和工作單元實現增刪查改

在MVC程式中,使用泛型倉儲模式和工作單元實現增刪查改

在這片文章中,我將自己動手為所有的實體:寫一個泛型倉儲類,還有一個工作單元。

工作單元的職責就是:為每一個實體,建立倉儲例項。倉儲(倉庫)的職責:增刪查改的功能實現。

我們將會在控制器中,建立工作單元類(UnitOfWork)的例項,然後根據實體,建立倉儲例項,再就是使用倉儲裡面的方法,做操作了。

下面的圖中,解釋了,倉儲和EF 資料上文的關係,在這個圖裡面,MVC控制器和倉儲之間的互動,是通過工作單元來進行的,而不是直接和EF接觸。

那麼你可能就要問了,為什麼要使用工作單元???

工作單元,就像其名稱一樣,做某些事情。在這篇文章中,工作單元主要是,建立例項:它例項化資料上下文,然後使用同樣的資料上下文物件,例項化每一個倉儲物件,用來做資料庫操作。所以: 工作單元是一種模式,它確保我們所有的倉儲類,使用同樣資料庫上下文。

實現一個泛型倉儲類和一個工作單元

請注意:在這篇文章中,你的使用者介面,使用具體的類,而不是介面,原因,我後面的一篇文章中,會說!

好了,現在開始實施:

在這篇文章中,我將會搭建4個專案。

MVC.Core---->>>類庫專案【這裡面:主要是實體的宣告】

MVC.Data---->>>類庫專案【這裡面主要是資料庫的操作,新增引用類庫專案(MVC.Core)】

MVC.Repository--->>>類庫專案【這裡主要是定義泛型倉儲類,新增引用MVC.Core和MVC.Data兩個專案】

MVC.Web----->>>MVC WEB程式【Web程式UI,新增引用MVC.Core和MVC.Data還有MVC.Repository三個專案】

框架的介面:

首先來寫實體層:在MVC.Core專案下,新增一個BaseEntity實體,新增如下程式碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVC.Core
{
    public class BaseEntity
    {
        /// <summary>
        /// 編號
        /// </summary>
        public
Int64 ID { get; set; } /// <summary> /// 新增時間 /// </summary> public DateTime AddedTime { get; set; } /// <summary> /// 修改時間 /// </summary> public DateTime ModifiedTime { get; set; } /// <summary> /// IP地址 /// </summary> public string IP { get; set; } } }

然後,在MVC.Core專案下,新建一個資料夾【Data】,在【Data】資料夾裡面新增Book實體,Book繼承BaseEntity實體。

下面是Book實體裡面的程式碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVC.Core.Data
{
   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 PublishedTime { get; set; }
    }
}

然後,我們看到MVC.Data這個類庫專案,這個專案中,我們將會包含資料上下文類,Book實體的對映。

ADO.NET Entity Framework 要求我們建立的資料上下文類必須繼承DbContext類。我們在上下文類中,將會重寫OnModelCreating方法,這個方法是用來使用Code-First方式,配置實體類的。

使用EF,需要安裝EF,這裡就不介紹了,要了解的話,可以去看我前面的文章中的介紹。

我建立一個EFDbContextClass類,下面是資料上下文類中的程式碼:

using MVC.Core;
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 MVC.Data
{
    public class EFDBContextClass:DbContext
    {
        public EFDBContextClass() : base("name=ConnectionStrings") 
        { 
        }
        public new IDbSet<TEntity> Set<TEntity>() where TEntity:BaseEntity
        {
            return base.Set<TEntity>();
        }

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

        
    }
}

在上面的程式碼中,使用反射來對映每個實體。然後資料上下文的建構函式中,我傳遞了連線字串名字ConnectionStrings,在配置檔案中

<connectionStrings>
    <add name="ConnectionStrings" connectionString="server=.;database=MyRepositoryDB;uid=sa;pwd=Password_1" providerName="System.Data.SqlClient"/>
  </connectionStrings>

好了,資料上下文也寫好了,現在開始實體對映吧,在MVC.Data專案中,新增一個資料夾--【Mapping】,然後在Mapping資料夾下面,新增一個類--【BookMap】,下面是BookMap的程式碼:

using MVC.Core.Data;
using System;
using System.Collections.Generic;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations.Schema;

namespace MVC.Data.Mapping
{
   public class BookMap:EntityTypeConfiguration<Book>
    {
       public BookMap()
       {
           //配置主鍵
           HasKey(s => s.ID);

           //配置列
           Property(s => s.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
           Property(p => p.Title).IsRequired();
           Property(p => p.ISBN).IsRequired();
           Property(p => p.Author).IsRequired();
           Property(p => p.AddedTime).IsRequired();
           Property(p => p.ModifiedTime).IsRequired();
           Property(p => p.PublishedTime).IsRequired();
           Property(p => p.IP);

           //配置表名稱
           ToTable("Books");
           
       }
    }
}

好了,現在開始寫我們的泛型倉儲類,在我們的MVC.Repository專案中,新增一個類Repository。這裡沒有新增介面倉儲,是為了更好的理解。泛型倉儲類擁有增刪查改的方法,

這個倉儲類,擁有一個帶資料上下文類的引數的建構函式,所以當我們建立建立倉儲例項的時候,只需要傳遞一個數據上下文物件過來就行,這就一樣,每個實體的倉儲都有一樣的資料上下文物件了。下面的程式碼是,泛型倉儲模式程式碼了:

using MVC.Core;
using MVC.Data;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVC.Repository
{
    public class Repository<T> where T:BaseEntity
    {
        private EFDBContextClass context;

        private IDbSet<T> entities;

      

        string errorMessage = string.Empty;

        public Repository(EFDBContextClass context)
        {
            this.context = context;
        }


       
        public T GetById(object id)
        {
            return this.Entities.Find(id);
        }

        public void Insert(T entity)
        {
            try
            {
                if (entity == null)
                {
                    throw new ArgumentNullException("entity");
                }
                this.Entities.Add(entity);
                this.context.SaveChanges();
            }
            catch (DbEntityValidationException ex)
            {

                //錯誤處理機制
                foreach (var validationErros in ex.EntityValidationErrors)
                {
                    foreach (var errorInfo in validationErros.ValidationErrors)
                    {
                        errorMessage += string.Format("屬性:{0} 錯誤訊息:{1}", errorInfo.PropertyName, errorInfo.ErrorMessage) 
+ Environment.NewLine;

                    }
                }
                throw new Exception(errorMessage, ex);
            }


        }

        public void Update(T entity)
        {
            try
            {
                if (entity == null)
                {
                    throw new ArgumentNullException("entity");
                }
                this.context.SaveChanges();
            }

            catch (DbEntityValidationException ex)
            {
                foreach (var errorItems in ex.EntityValidationErrors)
                {
                    foreach (var errorinfo in errorItems.ValidationErrors)
                    {
                        errorMessage += string.Format("屬性名:{0},錯誤訊息:{1}", errorinfo.PropertyName, errorinfo.ErrorMessage) 
+ Environment.NewLine;
                    }
                }
                throw new Exception(errorMessage, ex);
            }

        }

        public void Delete(T entity)
        { 
            try
            {
         
                if (entity == null)
                {
                    throw new ArgumentNullException("entity");
                }
                this.Entities.Remove(entity);
                this.context.SaveChanges();

            }
            catch (DbEntityValidationException ex)
            {
                foreach (var errorItems in ex.EntityValidationErrors)
                {
                    foreach (var errorinfo in errorItems.ValidationErrors)
                    {
                        errorMessage += string.Format("屬性名:{0},錯誤訊息:{1}", errorinfo.PropertyName, errorinfo.ErrorMessage) 
+ Environment.NewLine;
                    }
                }
                throw new Exception(errorMessage, ex);
            }
        }
        private IDbSet<T> Entities
        {
            get
            {
                if (entities == null)
                {
                    entities = context.Set<T>();
                }
                return entities;
            }
        }

        public virtual IQueryable<T> Table
        {
            get 
            {
                return this.Entities;
            }
        }

    }
}

在MVC.Repository專案中新增一個類:UnitOfWork

現在來為工作單元建立一個類,UnitOfWork,這個類繼承IDisposible介面,所以它的每個例項將會在控制器中銷燬,這個工作單元,初始化程式的資料上下文類【EFDBContextClass】.這個工作單元類的核心就是Repository方法,這個方法,為每一個繼承自BaseEntity的實體,返回一個倉儲【repository】物件,下面是工作單元類的程式碼:

using MVC.Core;
using MVC.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVC.Repository
{
   public class UnitOfWork:IDisposable
    {
       private readonly EFDBContextClass context;
       private bool disposed;
       private Dictionary<string, object> repositories;

       public UnitOfWork(EFDBContextClass context)
       {

           this.context = context;
       }
       public UnitOfWork()
       {
           context = new EFDBContextClass();
       }

       public void Dispose()
       {
           Dispose(true);
           GC.SuppressFinalize(this);
       }

       public void Save()
       {
           context.SaveChanges();
       }

       public virtual void Dispose(bool disposing)
       {
           if (!disposed)
           {
               if (disposing)
               {
                   context.Dispose();
               }
           }
           disposed = true;
       }

       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))
           {
               var repositoryType = typeof(Repository<>);
               var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), context);
               repositories.Add(type, repositoryInstance);
           }
           return (Repository<T>)repositories[type];
       }

    }
}

到此,現在底層的程式碼,基本都寫好了。

現在開始MVC.Web專案裡面的程式碼:

首先,我們在控制器資料夾下面,新建一個控制器類,BookController。然後,我們建立工作單元的例項。

下面是BookController控制器的程式碼:

using MVC.Core.Data;
using MVC.Data;
using MVC.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVC.Web.Controllers
{
    public class BookController : Controller
    {
        private UnitOfWork unitOfWork = new UnitOfWork();

        private Repository<Book> bookRepository;

        public BookController()
        {
            //通過工作單元來初始化倉儲
            bookRepository = unitOfWork.Repository<Book>();
        }
        // GET: Book
        public ActionResult Index()
        {
          List<Book> listBooks=  bookRepository.Table.ToList();
          return View(listBooks);
        }

        public ActionResult CreateEditBook(int? id)
        {
            Book bookModel = new Book();

            if (id.HasValue)
            {
                bookModel = bookRepository.GetById(id.Value);
            }
            return View(bookModel);
        }

        [HttpPost]
        public ActionResult CreateEditBook(Book model)
        {
            if (model.ID == 0)
            {
                model.ModifiedTime = DateTime.Now;
                model.AddedTime = DateTime.Now;
                model.IP = Request.UserHostAddress;
                bookRepository.Insert(model);
            }
            else
            {
                var editModel = bookRepository.GetById(model.ID);
                editModel.Title = model.Title;
                editModel.Author = model.Author;
                editModel.ISBN = model.ISBN;
                editModel.PublishedTime = model.PublishedTime;
                editModel.ModifiedTime = System.DateTime.Now;
                editModel.IP = Request.UserHostAddress;
                bookRepository.Update(editModel);
            
            }
            if (model.ID > 0)
            {
                return RedirectToAction("Index");
            }
            return View(model);
        }

        public ActionResult DeleteBook(int id)
        {
          Book model=  bookRepository.GetById(id);

          return View(model);
         

        }

        [HttpPost,ActionName("DeleteBook")]
        public ActionResult ConfirmDeleteBook(int id)
        {
          Book model=  bookRepository.GetById(id);

          bookRepository.Delete(model);
          return RedirectToAction("Index");
        }

        public ActionResult DetailBook(int id)
        {
          Book model=  bookRepository.GetById(id);

          return View(model);
        }
        protected override void Dispose(bool disposing)
        {
            unitOfWork.Dispose();
            base.Dispose(disposing);
        }
    }
}

現在已經完成了控制器的方法,開始新增檢視了:

CreateEdit:

@model MVC.Core.Data.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.PublishedTime,
                    new { @class = "col-lg-1 control-label" })
                    <div class="col-lg-9">
                        @Html.TextBoxFor(model => model.PublishedTime,
                        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
{
    <script src="~/Scripts/bootstrap-datepicker.js" type="text/javascript"></script>
    <script src="~/Scripts/book-create-edit.js" type="text/javascript"></script>
} 

DeleteBook:

@model MVC.Core.Data.Book

@{
    ViewBag.Title = "Delete Book";
}

<div class="book-example panel panel-primary">
    <div class="panel-heading panel-head">Delete Book</div>
    <div class="panel-body">
        <h3>Are you sure you want to delete this?</h3>
        <h1>@ViewBag.ErrorMessage</h1>
        <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.DisplayFor(model => model.Title,
                    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.DisplayFor(model => model.Author,
                    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.DisplayFor(model => model.ISBN,
                    new { @class = "form-control" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.PublishedTime,
                new { @class = "col-lg-1 control-label" })
                <div class="col-lg-9">
                    @Html.DisplayFor(model => model.PublishedTime,
                    new { @class = "form-control" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.AddedTime,
                new { @class = "col-lg-1 control-label" })
                <div class="col-lg-9">
                    @Html.DisplayFor(model => model.AddedTime,
                    new { @class = "form-control" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.ModifiedTime,
                new { @class = "col-lg-1 control-label" })
                <div class="col-lg-9">
                    @Html.DisplayFor(model => model.ModifiedTime,
                    new { @class = "form-control" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.IP,
                new { @class = "col-lg-1 control-label" })
                <div class="col-lg-9">
                    @Html.DisplayFor(model => model.IP,
                    new { @class = "form-control" })
                </div>
            </div>

            @using (Html.BeginForm())
            {
                <div class="form-group">
                    <div class="col-lg-1"></div>
                    <div class="col-lg-9">
                        <input type="submit" value="Delete"
                               class="btn btn-danger" />
                        @Html.ActionLink("Back to List", "Index",
                        null, new { @class = "btn btn-success" })
                    </div>
                </div>
            }
        </div>
    </div>
</div