MVC3+EF4.1學習系列(八)-----利用Repository and Unit of Work重構項目

來源:互聯網
上載者:User

標籤:總結   事務   sql語句   hang   list   arc   控制器   itop   sql   

項目最基礎的東西已經結束了,但是現在我們的項目還不健全  不利於測試 重複性代碼多   層與層之間耦合性高  不利於擴充等問題.今天的這章 主要就是解決這些問題的。再解決這些問題時,自己也產生了很多疑問,理解的也並不是很透徹 ,希望我的疑問能在這裡得到解答~~

 

一.模式介紹

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和不使用的區別

二.改造開始

讓我們回到最早的學生控制器上 忘了講啥的朋友可以看下這節-------學生控制器    我們把

private SchoolContext db = new SchoolContext();

寫在了每個控制器裡面  聲明周期與控制器完全耦合在了一起

1.建立學生資產庫介面

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using ContosoUniversity.Models;

namespace ContosoUniversity.DAL
{
public interface IStudentRepository : IDisposable
{
IEnumerable<Student> 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
{
public class 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);
}

public void InsertStudent(Student student)
{
context.Students.Add(student);
}

public void DeleteStudent(int studentID)
{
Student student = context.Students.Find(studentID);
context.Students.Remove(student);
}

public void UpdateStudent(Student student)
{
context.Entry(student).State = EntityState.Modified;
}

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

private bool disposed = false;

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

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

3.改造學生控制器

View Code

通過上面的操作  現在我們的控制器  針對的是一個介面 而不再是直接通過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
{
public class GenericRepository<TEntity> where TEntity : class
{
internal SchoolContext context;
internal DbSet<TEntity> dbSet;

public GenericRepository(SchoolContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}

public virtual 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
(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}

if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}

public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}

public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}

public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}

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

public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
}

這裡重點講這個方法

public virtual 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
(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}

if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}

還是如上面所說 我覺得這裡應該返回的是 IQueryable 所以我覺得應該 去掉最後的 .ToList  並且返回IQueryable 

然後 來看這個方法  第一個接受一個運算式樹狀架構   其實就是過濾條件  第二個 是個委託 主要是用來排序的   第三個接受要貪婪載入哪些導覽屬性 可以用逗號隔開

並且 這裡利用了下4.0的功能 可以給參數個預設值  都是空  怎麼用這個方法 後面會寫的有~~

 

還有個說的地方 以前我沒有提到過   這裡稍帶的說下

     public virtual void 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
{
public class 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;
}
}

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

private bool disposed = false;

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

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

把想讓unit of work 幫你整體控制的類寫到裡面 這裡 我們唯寫了兩個

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

最後是儲存和資源的釋放

好 現在看下怎麼用這個~

五.改變課程式控制制器

原文控制器代碼

原文的這個demo 並沒有很好的體現出 使用 unit of work 的好處  因為他的這個例子沒有出現一個邏輯用到多個資產庫的  希望大家明白這點~~ 等以後的文章

我會寫個完整的demo  來說明這點 這裡大家先看下 明白怎麼回事就行~

六.EF+MVC架構的疑問

1.是否有必要實現 IUnitOfWork 介面?代碼大概這樣

 

public interface IUnitOfWork

{

void Save();

IStudentRepository StudentRepository { get; }

}

 

實現介面

public class 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;

}

}

public void 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語句,關閉跟蹤狀態這些~~

MVC3+EF4.1學習系列(八)-----利用Repository and Unit of Work重構項目

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.