您的位置:首页 > 其它

在MVC程序中,使用泛型仓储模式和工作单元实现增删查改

2016-03-07 08:48 633 查看
在这片文章中,我将自己动手为所有的实体:写一个泛型仓储类,还有一个工作单元。

工作单元的职责就是:为每一个实体,创建仓储实例。仓储(仓库)的职责:增删查改的功能实现。

我们将会在控制器中,创建工作单元类(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>


效果图:









内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: