你的位置:首页 > ASP.net教程

[ASP.net教程]在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> 

DetailBook:

@model MVC.Core.Data.Book@{  ViewBag.Title = "Detail Book";}<div class="book-example panel panel-primary">  <div class="panel-heading panel-head">Book Detail</div>  <div class="panel-body">    <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">            @Html.ActionLink("Edit", "CreateEditBook",            new { id = Model.ID }, new { @class = "btn btn-primary" })            @Html.ActionLink("Back to List", "Index",            null, new { @class = "btn btn-success" })          </div>        </div>      }    </div>  </div></div>  

Index:

@model IEnumerable<MVC.Core.Data.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> 

效果图: