博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
转载Repository 和Unit of work的使用说明
阅读量:5332 次
发布时间:2019-06-15

本文共 18781 字,大约阅读时间需要 62 分钟。

项目最基础的东西已经结束了,但是现在我们的项目还不健全  不利于测试 重复性代码多   层与层之间耦合性高  不利于扩展等问题.今天的这章 主要就是解决这些问题的。再解决这些问题时,自己也产生了很多疑问,理解的也并不是很透彻 ,希望我的疑问能在这里得到解答~~

 

一.模式介绍

1.Repository

在《企业架构模式》中,通过用来访问领域对象的一个类似集合的接口,在领域与数据映射层之间进行协调。还有请大家参考这个 

然后说下我对这个的感觉和疑问   怎么都觉得这个Repository就是以前的dao(dal)层~~  不过就是通过接口 泛型 与ORM结合 实现了更好的复用 不知道对不~~

2.Unit of Work

先上介绍    。主要是解决当有多个操作时,数据的变更 存储 以及事务的处理等 。unit of work是一个记录所有对象模型修改过的信息,在提交的时候,一次性修改,并把结果同步到数据库。 其实我们可以发现 DbContext 已经具备了这样的功能~~ 很大程度实现了Unit of work~  因为我们savechange()

时 才提交数据的

3.整体概述

运用Repository 和Unit of Work 在 数据层  和业务逻辑层之间 再创建一个抽象层 。它将帮我们隔离变化,并且更利于测试.

下面借用下原文的图 说明下使用Repository 和Unit of Work和不使用的区别

二.改造开始

让我们回到最早的学生控制器上 忘了讲啥的朋友可以看下这节-------    我们把

private SchoolContext db =new SchoolContext();

写在了每个控制器里面  声明周期与控制器完全耦合在了一起

1.创建学生资源库接口

using System; using System.Collections.Generic; using System.Linq; using System.Web; using ContosoUniversity.Models; namespace ContosoUniversity.DAL {     publicinterface IStudentRepository : IDisposable     {         IEnumerable
GetStudents(); Student GetStudentByID(int studentId); void InsertStudent(Student student); void DeleteStudent(int studentID); void UpdateStudent(Student student); void Save(); } }

2.实现接口

using System; using System.Collections.Generic; using System.Linq; using System.Data; using ContosoUniversity.Models; namespace ContosoUniversity.DAL {     publicclass StudentRepository : IStudentRepository, IDisposable     {         private SchoolContext context; public StudentRepository(SchoolContext context)         {             this.context = context;         } public IEnumerable
GetStudents() { return context.Students.ToList(); } public Student GetStudentByID(int id) { return context.Students.Find(id); } publicvoid InsertStudent(Student student) { context.Students.Add(student); } publicvoid DeleteStudent(int studentID) { Student student = context.Students.Find(studentID); context.Students.Remove(student); } publicvoid UpdateStudent(Student student) { context.Entry(student).State = EntityState.Modified; } publicvoid Save() { context.SaveChanges(); } privatebool disposed =false; protectedvirtualvoid Dispose(bool disposing) { if (!this.disposed) { if (disposing) { context.Dispose(); } } this.disposed =true; } publicvoid Dispose() { Dispose(true); GC.SuppressFinalize(this); } } }

3.改造学生控制器

View Code
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Web; using System.Web.Mvc; using ContosoUniversity.Models; using ContosoUniversity.DAL; using PagedList; namespace ContosoUniversity.Controllers {     publicclass StudentController : Controller     {         private IStudentRepository studentRepository; public StudentController()         {             this.studentRepository =new StudentRepository(new SchoolContext());         } public StudentController(IStudentRepository studentRepository)         {             this.studentRepository = studentRepository;         } //// GET: /Student/public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)         {             ViewBag.CurrentSort = sortOrder;             ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ?"Name desc" : "";             ViewBag.DateSortParm = sortOrder =="Date"?"Date desc" : "Date"; if (Request.HttpMethod =="GET")             {                 searchString = currentFilter;             }             else             {                 page =1;             }             ViewBag.CurrentFilter = searchString;                          var students = from s in studentRepository.GetStudents()                            select s;             if (!String.IsNullOrEmpty(searchString))             {                 students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())                                        || s.FirstMidName.ToUpper().Contains(searchString.ToUpper()));             }             switch (sortOrder)             {                 case"Name desc":                     students = students.OrderByDescending(s => s.LastName);                     break;                 case"Date":                     students = students.OrderBy(s => s.EnrollmentDate);                     break;                 case"Date desc":                     students = students.OrderByDescending(s => s.EnrollmentDate);                     break;                 default:                     students = students.OrderBy(s => s.LastName);                     break;             } int pageSize =3;             int pageIndex = (page ??1) -1;             return View(students.ToPagedList(pageIndex, pageSize));         } //// GET: /Student/Details/5public ViewResult Details(int id)         {             Student student = studentRepository.GetStudentByID(id);             return View(student);         } //// GET: /Student/Createpublic ActionResult Create()         {             return View();         } //// POST: /Student/Create         [HttpPost]         public ActionResult Create(Student student)         {             try             {                 if (ModelState.IsValid)                 {                     studentRepository.InsertStudent(student);                     studentRepository.Save();                     return RedirectToAction("Index");                 }             }             catch (DataException)             {                 //Log the error (add a variable name after DataException)                ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");             }             return View(student);         } //// GET: /Student/Edit/5public ActionResult Edit(int id)         {             Student student = studentRepository.GetStudentByID(id);             return View(student);         } //// POST: /Student/Edit/5         [HttpPost]         public ActionResult Edit(Student student)         {             try             {                 if (ModelState.IsValid)                 {                     studentRepository.UpdateStudent(student);                     studentRepository.Save();                     return RedirectToAction("Index");                 }             }             catch (DataException)             {                 //Log the error (add a variable name after DataException)                ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");             }             return View(student);         } //// GET: /Student/Delete/5public ActionResult Delete(int id, bool? saveChangesError)         {             if (saveChangesError.GetValueOrDefault())             {                 ViewBag.ErrorMessage ="Unable to save changes. Try again, and if the problem persists see your system administrator.";             }             Student student = studentRepository.GetStudentByID(id);             return View(student);         } //// POST: /Student/Delete/5         [HttpPost, ActionName("Delete")]         public ActionResult DeleteConfirmed(int id)         {             try             {                 Student student = studentRepository.GetStudentByID(id);                 studentRepository.DeleteStudent(id);                 studentRepository.Save();             }             catch (DataException)             {                 //Log the error (add a variable name after DataException)return RedirectToAction("Delete",                     new System.Web.Routing.RouteValueDictionary {                  { "id", id },                  { "saveChangesError", true } });             }             return RedirectToAction("Index");         } protectedoverridevoid Dispose(bool disposing)         {             studentRepository.Dispose();             base.Dispose(disposing);         }     } }

通过上面的操作  现在我们的控制器  针对的是一个接口 而不再是直接通过dbContext   当然 如果我们这里使用IOC容器  将能实现更好的解耦 大家可以看下

桀骜的灵魂的

4.改造后出现的问题

1.我们本来在控制器里实现的dbContext,  现在这个被放在了Repository里   因为项目会有多个Repository  所以会有多个dbContext!  这是个时候 当我们有

一个复杂的逻辑   要用到多个Repository时  比如 我随便说个例子~~  比如 添加个订单 更新个详细订单 再删除个客户 假设这一系列的逻辑 是在一起的  这里就会用到三个 Repository    我们要调用不同的Repository  里的 SAVE()    这时 我们就要做多次提交 而且不再是统一的事务了  效率 完整性 都大大的下降了。反而失去了

dbContext 自带 Unit of Work的好处~~   没关系  后面我们会再实现Unit of Work 来改造这个问题

2.第二个问题 也是个很严重的问题 我们以前的时候  再查找学生  搜索 排序 以及分页时  是这样的

var students = from s in context.Students                select s; if (!String.IsNullOrEmpty(searchString)) {     students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())                            || s.FirstMidName.ToUpper().Contains(searchString.ToUpper())); }

而现在变成了

var students = from s in studentRepository.GetStudents()                select s;

这有个很严重的问题 以前是 IQueryable 而现在的是  IEnumerable  变成了数据直接全部查找出来  所以再这里 我觉得Repository的查找 应该是返回IQueryable

而不是  IEnumerable 

要不然 就出现了我最早在文中的疑问  这不就是普通的CRUD 一个普通的数据访问层而已   

Repository用法我觉得应该是 返回IQueryable 参数的接受应该是一个表达式树  不知道大家是否认同?希望大家帮我解决下疑惑 谢谢~

再下面的 公共泛型 Repository 会体现这个

三.创建一个公共的Repository

看看我们上面的学生Repository   如果我们再写 课程 院系 等等 这些代码 会非常类似  所以我们利用泛型注入 来实现复用  这里应该实现一个泛型接口 和泛型类

但是原文没有实现接口~ 只有个类

让我来看下这个类

namespace ContosoUniversity.DAL {     publicclass GenericRepository
where TEntity : class { internal SchoolContext context; internal DbSet
dbSet; public GenericRepository(SchoolContext context) { this.context = context; this.dbSet = context.Set
(); } publicvirtual IEnumerable
Get( Expression
> filter =null, Func
, IOrderedQueryable
> orderBy =null, string includeProperties ="") { IQueryable
query = dbSet; if (filter !=null) { query = query.Where(filter); } foreach (var includeProperty in includeProperties.Split (newchar[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { query = query.Include(includeProperty); } if (orderBy !=null) { return orderBy(query).ToList(); } else { return query.ToList(); } } publicvirtual TEntity GetByID(object id) { return dbSet.Find(id); } publicvirtualvoid Insert(TEntity entity) { dbSet.Add(entity); } publicvirtualvoid Delete(object id) { TEntity entityToDelete = dbSet.Find(id); Delete(entityToDelete); } publicvirtualvoid Delete(TEntity entityToDelete) { if (context.Entry(entityToDelete).State == EntityState.Detached) { dbSet.Attach(entityToDelete); } dbSet.Remove(entityToDelete); } publicvirtualvoid Update(TEntity entityToUpdate) { dbSet.Attach(entityToUpdate); context.Entry(entityToUpdate).State = EntityState.Modified; } } }

这里重点讲这个方法

publicvirtual IEnumerable
Get( Expression
> filter =null, Func
, IOrderedQueryable
> orderBy =null, string includeProperties ="") { IQueryable
query = dbSet; if (filter !=null) { query = query.Where(filter); } foreach (var includeProperty in includeProperties.Split (newchar[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { query = query.Include(includeProperty); } if (orderBy !=null) { return orderBy(query).ToList(); } else { return query.ToList(); } }

还是如上面所说 我觉得这里应该返回的是 IQueryable 所以我觉得应该 去掉最后的 .ToList  并且返回IQueryable 

然后 来看这个方法  第一个接受一个表达式树   其实就是过滤条件  第二个 是个委托 主要是用来排序的   第三个接受要贪婪加载哪些导航属性 可以用逗号隔开

并且 这里利用了下4.0的功能 可以给参数个默认值  都是空  怎么用这个方法 后面会写的有~~

 

还有个说的地方 以前我没有提到过   这里稍带的说下

publicvirtualvoid Delete(TEntity entityToDelete)         {             if (context.Entry(entityToDelete).State == EntityState.Detached)             {                 dbSet.Attach(entityToDelete);             }             dbSet.Remove(entityToDelete);         }

这个 dbSet.Attach(entityToDelete);  表示将对象添加到数据库上下文中   受dbcontext管理  这里我有个小疑问  不加这个判断 是否也可以 加这个的好处是?

四.创建 Unit of Work Class

创建这个类的主要目的  就是为了确保 多个Repository可以共享一个 database context  让我们看下这个类

namespace ContosoUniversity.DAL {     publicclass UnitOfWork : IDisposable     {         private SchoolContext context =new SchoolContext();         private GenericRepository
departmentRepository; private GenericRepository
courseRepository; public GenericRepository
DepartmentRepository { get {
if (this.departmentRepository ==null) { this.departmentRepository =new GenericRepository
(context); } return departmentRepository; } } public GenericRepository
CourseRepository { get {
if (this.courseRepository ==null) { this.courseRepository =new GenericRepository
(context); } return courseRepository; } } publicvoid Save() { context.SaveChanges(); } privatebool disposed =false; protectedvirtualvoid Dispose(bool disposing) { if (!this.disposed) { if (disposing) { context.Dispose(); } } this.disposed =true; } publicvoid Dispose() { Dispose(true); GC.SuppressFinalize(this); } } }

把想让unit of work 帮你整体控制的类写到里面 这里 我们只写了两个

private SchoolContext context =new SchoolContext(); private GenericRepository
departmentRepository; private GenericRepository
courseRepository;

实现只读属性

public GenericRepository
DepartmentRepository { get {
if (this.departmentRepository ==null) { this.departmentRepository =new GenericRepository
(context); } return departmentRepository; } }

最后是保存和资源的释放

好 现在看下怎么用这个~

五.改变课程控制器

原文控制器代码
namespace ContosoUniversity.Controllers {      publicclass CourseController : Controller     {         private UnitOfWork unitOfWork =new UnitOfWork(); //// GET: /Course/public ViewResult Index()         {             var courses = unitOfWork.CourseRepository.Get(includeProperties: "Department");             return View(courses.ToList());         } //// GET: /Course/Details/5public ViewResult Details(int id)         {             Course course = unitOfWork.CourseRepository.GetByID(id);             return View(course);         } //// GET: /Course/Createpublic ActionResult Create()         {             PopulateDepartmentsDropDownList();             return View();         }         [HttpPost]         public ActionResult Create(Course course)         {             try             {                 if (ModelState.IsValid)                 {                     unitOfWork.CourseRepository.Insert(course);                     unitOfWork.Save();                     return RedirectToAction("Index");                 }             }             catch (DataException)             {                 //Log the error (add a variable name after DataException)                ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");             }             PopulateDepartmentsDropDownList(course.DepartmentID);             return View(course);         } public ActionResult Edit(int id)         {             Course course = unitOfWork.CourseRepository.GetByID(id);             PopulateDepartmentsDropDownList(course.DepartmentID);             return View(course);         }         [HttpPost]         public ActionResult Edit(Course course)         {             try             {                 if (ModelState.IsValid)                 {                     unitOfWork.CourseRepository.Update(course);                     unitOfWork.Save();                     return RedirectToAction("Index");                 }             }             catch (DataException)             {                 //Log the error (add a variable name after DataException)                ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");             }             PopulateDepartmentsDropDownList(course.DepartmentID);             return View(course);         } privatevoid PopulateDepartmentsDropDownList(object selectedDepartment =null)         {             var departmentsQuery = unitOfWork.DepartmentRepository.Get(                 orderBy: q => q.OrderBy(d => d.Name));             ViewBag.DepartmentID =new SelectList(departmentsQuery, "DepartmentID", "Name", selectedDepartment);         } //// GET: /Course/Delete/5public ActionResult Delete(int id)         {             Course course = unitOfWork.CourseRepository.GetByID(id);             return View(course);         } //// POST: /Course/Delete/5         [HttpPost, ActionName("Delete")]         public ActionResult DeleteConfirmed(int id)         {             Course course = unitOfWork.CourseRepository.GetByID(id);             unitOfWork.CourseRepository.Delete(id);             unitOfWork.Save();             return RedirectToAction("Index");         } protectedoverridevoid Dispose(bool disposing)         {             unitOfWork.Dispose();             base.Dispose(disposing);         }     } }

原文的这个demo 并没有很好的体现出 使用 unit of work 的好处  因为他的这个例子没有出现一个逻辑用到多个资源库的  希望大家明白这点~~ 等以后的文章

我会写个完整的demo  来说明这点 这里大家先看下 明白怎么回事就行~

六.EF+MVC框架的疑问

1.是否有必要实现 IUnitOfWork 接口?代码大概这样

 

publicinterface IUnitOfWork {
void Save(); IStudentRepository StudentRepository { get; } }

 

实现接口

publicclass UnitOfWork : IUnitOfWork {
private SchoolEntities context =new SchoolEntities(); private IStudentRepository studentRepository; public IStudentRepository StudentRepository {
get {
if (this.studentRepository ==null) {
this.studentRepository =new StudentRepository(context); } return studentRepository; } } publicvoid Save() {
context.SaveChanges(); } }

控制器

private IUnitOfWork unitOfWork; public StudentController () : this (new UnitOfWork()) {
} public StudentController (IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork; }

2. 是否有必要再  加上一个 服务层 service  这个层 在 控制器和dal 之间   也就是 通过 unitofwork 调用的东西等 都写在servie上  这样控制器里的代码会变得非常少, 个人觉得应该加上service层的,但是是否需要加Iservice 接口    加这个接口 能获得哪些好处 ? 我一直觉得 只用 给数据访问层 实现接口 就行  ~~

3. 这里我们是用的unit of work 完成了事务的一致性  以前我是使用

using (TransactionScope transaction =new TransactionScope()){    ....      transaction.Complete();    }

用这个来实现 事务一致性  不知道大家觉得 这个和 unit of work 比怎么样? 我暂时还没研究这个~     但是 对这个做了很好的介绍 感谢小城~ 大家可以参考他的

4. 我们的缓存 比如用的 mongodb 这个写到哪层比较好呢?

六.总结

经过重构 代码终于有些项目的样子了~~   下节讲讲EF的其他一些功能 如 直接执行SQL语句,关闭跟踪状态这些~~

 
 
分类: ,
标签:

转载于:https://www.cnblogs.com/wphl-27/p/3407967.html

你可能感兴趣的文章
【转载】Abstract Factory Step by Step --- 抽象工厂
查看>>
copy file
查看>>
UI控件
查看>>
myeclipse配置Spring
查看>>
webp图片实践之路
查看>>
「一本通 1.2 练习 3」灯泡 (loj10016)
查看>>
JAVA 之 GC 二
查看>>
less
查看>>
深度学习激活函数们
查看>>
极其平凡的一天——3.19
查看>>
12306购票
查看>>
[笔记] 走进 Pocket,看看只有 20 位员工的 Pocket 是如何搞定 2000 万用户的
查看>>
HMTL—表单
查看>>
C++标准库头文件找不到的问题
查看>>
EF延迟加载和懒加载
查看>>
ftrace 使用方法
查看>>
IPSec的高可用性技术
查看>>
转发-【分享】思科无线控制器HA模式升级
查看>>
sql server sql查询数据库的表,字段,主键,自增,字段类型等信息
查看>>
Timus 1090 树状数组
查看>>