文章索引和简介

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

 

一.模式介绍

1.Repository

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

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

2.Unit of Work

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

时 才提交数据的

3.整体概述

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

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

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

二.改造开始

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

private SchoolContext db =new SchoolContext();

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

1.创建学生资源库接口

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
using System; using System.Collections.Generic; using System.Linq; using System.Web; using ContosoUniversity.Models;
namespace ContosoUniversity.DAL { publicinterface IStudentRepository : IDisposable { IEnumerable<Student> GetStudents(); Student GetStudentByID(int studentId); void InsertStudent(Student student); void DeleteStudent(int studentID); void UpdateStudent(Student student); void Save(); } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

2.实现接口

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
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<Student> 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); } } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

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.第二个问题 也是个很严重的问题 我们以前的时候  再查找学生  搜索 排序 以及分页时  是这样的

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
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())); }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

而现在变成了

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

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

而不是  IEnumerable 

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

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

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

三.创建一个公共的Repository

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

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

让我来看下这个类

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
namespace ContosoUniversity.DAL {     publicclass GenericRepository<TEntity>where TEntity : class     {         internal SchoolContext context;         internal DbSet<TEntity> dbSet;
public GenericRepository(SchoolContext context) { this.context = context; this.dbSet = context.Set<TEntity>(); }
publicvirtual IEnumerable<TEntity> Get( Expression<Func<TEntity, bool>> filter =null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy =null, string includeProperties ="") { IQueryable<TEntity> 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; } } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

这里重点讲这个方法

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
publicvirtual IEnumerable<TEntity> Get(             Expression<Func<TEntity, bool>> filter =null,             Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy =null,             string includeProperties ="")         {             IQueryable<TEntity> 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(); } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

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

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

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

 

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

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
publicvirtualvoid Delete(TEntity entityToDelete)         {             if (context.Entry(entityToDelete).State == EntityState.Detached)             {                 dbSet.Attach(entityToDelete);             }             dbSet.Remove(entityToDelete);         }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

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

四.创建 Unit of Work Class

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

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
namespace ContosoUniversity.DAL {     publicclass UnitOfWork : IDisposable     {         private SchoolContext context =new SchoolContext();         private GenericRepository<Department> departmentRepository;         private GenericRepository<Course> courseRepository;
public GenericRepository<Department> DepartmentRepository { get {
if (this.departmentRepository ==null) { this.departmentRepository =new GenericRepository<Department>(context); } return departmentRepository; } }
public GenericRepository<Course> CourseRepository { get {
if (this.courseRepository ==null) { this.courseRepository =new GenericRepository<Course>(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); } } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

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

private SchoolContext context =new SchoolContext(); private GenericRepository<Department> departmentRepository; private GenericRepository<Course> courseRepository;

实现只读属性

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
public GenericRepository<Department> DepartmentRepository {     get     {
if (this.departmentRepository ==null) { this.departmentRepository =new GenericRepository<Department>(context); } return departmentRepository; } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

最后是保存和资源的释放

好 现在看下怎么用这个~

五.改变课程控制器

原文控制器代码
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 接口?代码大概这样

 

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
publicinterface IUnitOfWork
{
void Save();
IStudentRepository StudentRepository {
get; }
}
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

 

实现接口

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
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();
}
}
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

控制器

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
private IUnitOfWork unitOfWork;
public StudentController () : this (new UnitOfWork())
{
}
public StudentController (IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

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语句,关闭跟踪状态这些~~

 
 
MVC
Entity Framework

文章索引和简介

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

 

一.模式介绍

1.Repository

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

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

2.Unit of Work

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

时 才提交数据的

3.整体概述

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

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

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

二.改造开始

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

private SchoolContext db =new SchoolContext();

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

1.创建学生资源库接口

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
using System; using System.Collections.Generic; using System.Linq; using System.Web; using ContosoUniversity.Models;
namespace ContosoUniversity.DAL { publicinterface IStudentRepository : IDisposable { IEnumerable<Student> GetStudents(); Student GetStudentByID(int studentId); void InsertStudent(Student student); void DeleteStudent(int studentID); void UpdateStudent(Student student); void Save(); } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

2.实现接口

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
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<Student> 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); } } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

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.第二个问题 也是个很严重的问题 我们以前的时候  再查找学生  搜索 排序 以及分页时  是这样的

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
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())); }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

而现在变成了

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

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

而不是  IEnumerable 

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

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

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

三.创建一个公共的Repository

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

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

让我来看下这个类

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
namespace ContosoUniversity.DAL {     publicclass GenericRepository<TEntity>where TEntity : class     {         internal SchoolContext context;         internal DbSet<TEntity> dbSet;
public GenericRepository(SchoolContext context) { this.context = context; this.dbSet = context.Set<TEntity>(); }
publicvirtual IEnumerable<TEntity> Get( Expression<Func<TEntity, bool>> filter =null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy =null, string includeProperties ="") { IQueryable<TEntity> 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; } } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

这里重点讲这个方法

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
publicvirtual IEnumerable<TEntity> Get(             Expression<Func<TEntity, bool>> filter =null,             Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy =null,             string includeProperties ="")         {             IQueryable<TEntity> 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(); } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

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

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

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

 

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

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
publicvirtualvoid Delete(TEntity entityToDelete)         {             if (context.Entry(entityToDelete).State == EntityState.Detached)             {                 dbSet.Attach(entityToDelete);             }             dbSet.Remove(entityToDelete);         }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

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

四.创建 Unit of Work Class

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

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
namespace ContosoUniversity.DAL {     publicclass UnitOfWork : IDisposable     {         private SchoolContext context =new SchoolContext();         private GenericRepository<Department> departmentRepository;         private GenericRepository<Course> courseRepository;
public GenericRepository<Department> DepartmentRepository { get {
if (this.departmentRepository ==null) { this.departmentRepository =new GenericRepository<Department>(context); } return departmentRepository; } }
public GenericRepository<Course> CourseRepository { get {
if (this.courseRepository ==null) { this.courseRepository =new GenericRepository<Course>(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); } } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

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

private SchoolContext context =new SchoolContext(); private GenericRepository<Department> departmentRepository; private GenericRepository<Course> courseRepository;

实现只读属性

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
public GenericRepository<Department> DepartmentRepository {     get     {
if (this.departmentRepository ==null) { this.departmentRepository =new GenericRepository<Department>(context); } return departmentRepository; } }
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

最后是保存和资源的释放

好 现在看下怎么用这个~

五.改变课程控制器

原文控制器代码
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 接口?代码大概这样

 

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
publicinterface IUnitOfWork
{
void Save();
IStudentRepository StudentRepository {
get; }
}
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

 

实现接口

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
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();
}
}
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

控制器

转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目
private IUnitOfWork unitOfWork;
public StudentController () : this (new UnitOfWork())
{
}
public StudentController (IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
转载Repository 和Unit of work的使用说明
    

利用Repository and Unit of Work重构项目

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语句,关闭跟踪状态这些~~

相关文章: