11

我已經閱讀了許多關於Stackoverflow的帖子,因爲我可以在 之內找到關於使用工作單元模式的ASP.Net MVC 3應用程序,其中包含一個業務層。然而,我仍然有幾個問題與 關於這個話題,並將不勝感激人們可以給我的任何反饋。我應該在ASP.Net MVC 3應用程序中創建工作單元實例?

我正在開發一個使用EF 4.1的ASP.Net MVC 3 Web應用程序。我將使用兩種存儲庫和 這個項目相似,它們是如何在this偉大的教程使用的單位工作模式的

在我的項目不同的是,我還需要包括業務層(單獨的項目,在我解決方案)以便 執行應用程序的各種業務規則。上面提到的教程沒有一個業務層和 因此從控制器創建工作類的單位的實例

public class CourseController : Controller 
{ 
    private UnitOfWork unitOfWork = new UnitOfWork(); 

不過,我的問題是,我應該創建工作單元的實例如果我有一個業務層的課程?

我個人認爲它應該在我的控制器被創建,然後注入到業務層,像這樣:

public class PeopleController : Controller 
{ 
    private readonly IUnitOfWork _UoW; 
    private IPersonService _personService; 

    public PeopleController() 
    { 
     _UoW = new UnitOfWork(); 
     _personService = new PersonService(_UoW); 
    } 

    public PeopleController(IUnitOfWork UoW, IPersonService personService) 
    { 
     _UoW = UoW; 
     _personService = personService; 

    } 

    public ActionResult Edit(int id) 
    { 
     Person person = _personService.Edit(id); 
     return View(person); 
    } 

public class UnitOfWork : IUnitOfWork, IDisposable 
{ 
    private BlogEntities _context = new BlogEntities(); 
    private PersonRepository personRepository = null; 

    public IPersonRepository PersonRepository 
    { 
     get 
     { 

      if (this.personRepository == null) 
      { 
       this.personRepository = new PersonRepository(_context); 
      } 
      return personRepository; 
     } 
    } 

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


public class PersonService : IPersonService 
{ 
    private readonly IUnitOfWork _UoW; 

    public PersonService(IUnitOfWork UoW) 
    { 
     _UoW = UoW; 
    } 

    public Person Edit(int id) 
    { 
     Person person = _UoW.PersonRepository.GetPersonByID(id); 
     return person; 
    } 

public class PersonRepository : IPersonRepository 
{ 
    private readonly BlogEntities _context; 

    public PersonRepository(BlogEntities context) 
    { 
     _context = context; 
    } 

    public Person GetPersonByID(int ID) 
    { 
     return _context.People.Where(p => p.ID == ID).Single(); 
    } 

我看過別人說,工作實例的單位不應該在控制器,但改爲在服務層 中創建。我對此方法不太確定的原因是因爲我的控制器可能必須在一個業務事務中使用幾個不同的服務層,並且如果在每個服務中創建工作單元實例,則會導致多個 單元的正在創建的工作實例違背了目標,即每個業務事務中的一個工作單元。或許我上面解釋過的是錯誤的,但如果是這樣的話,我會非常感謝,如果有人能把我說得對。

再次感謝您的幫助。

回答

15

我認爲你有幾個變化使:。

  1. 允許你的DI容器注入UnitOfWork實例到您的服務類在它們的構造函數,並把它從你的控制器完全。

  2. 如果您的DI容器支持它(例如Ninject),請將您的UnitOfWork配置爲基於每個請求進行管理;通過這種方式,您的服務將針對每個請求分別提交一個不同的UnitOfWork,並且您都完成了。還是......

  3. 如果您的DI容器不支持每個請求的壽命,其配置爲管理UnitOfWork作爲一個單身,所以每一個Service類獲取相同的實例。然後更新您的UnitOfWork以將其Entities對象存儲在以每個請求爲基礎存儲對象的數據存儲中,例如HttpContext.Current.Items,如here所述。

編輯1

關於其中UnitOfWork應注射;我會說服務層是正確的地方。如果您將系統想象爲外層處理用戶交互的一系列圖層,並且較低層處理數據存儲,則每層應該不太關心用戶,更關心數據存儲。 UnitOfWork是來自「較低層」層之一的概念,而控制器來自較高層;你的Service層適合他們之間。因此,將UnitOfWork加入Service類而不是Controller是有意義的。

編輯2

闡述一下UnitOfWork創作和它的關係HttpContext.Current.Items

UnitOfWork將不再持有一個Entities對象的引用,將通過HttpContext對象來完成,在這樣的接口後注入UnitOfWork

public interface IPerRequestDataStore : IDisposable 
{ 
    bool Contains(string key); 

    void Store<T>(string key, T value); 

    T Get<T>(string key); 
} 

然後HttpContext對象將實現IPerRequestDataStore這樣的:

public class StaticHttpContextPerRequestDataStore : IPerRequestDataStore 
{ 
    public bool Contains(string key) 
    { 
     return HttpContext.Current.Items.Contains(key); 
    } 

    public void Store<T>(string key, T value) 
    { 
     HttpContext.Current.Items[key] = value; 
    } 

    public T Get<T>(string key) 
    { 
     if (!this.Contains(key)) 
     { 
      return default(T); 
     } 

     return (T)HttpContext.Current.Items[key]; 
    } 

    public void Dispose() 
    { 
     var disposables = HttpContext.Current.Items.Values.OfType<IDisposable>(); 

     foreach (var disposable in disposables) 
     { 
      disposable.Dispose(); 
     } 
    } 
} 

順便說一句,因爲它使用的靜態HttpContext.Current財產,我把它叫做StaticHttpContextPerRequestDataStore;這對於單元測試來說並不理想(完全是另一個話題),但至少該名稱表明了它的依賴性質。

你的UnitOfWork然後通過IPerRequestDataStore它的每個Repository對象,因此他們可以訪問Entities;這意味着無論您創建了多少個UnitOfWork實例,都會在整個請求中使用相同的Entities對象,因爲它在IPerRequestDataStore中存儲和檢索。

你最好有一個抽象基Repository它將使用其IPerRequestDataStore懶加載其Entities對象是這樣的:

public abstract class RepositoryBase : IDisposable 
{ 
    private readonly IPerRequestDataStore _dataStore; 

    private PersonRepository personRepository; 

    protected RepositoryBase(IPerRequestDataStore dataStore) 
    { 
     this._dataStore = dataStore; 
    } 

    protected BlogEntities Context 
    { 
     get 
     { 
      const string contextKey = "context"; 

      if (!this._dataStore.Contains(contextKey)) 
      { 
       this._dataStore.Store(contextKey, new BlogEntities()); 
      } 

      return this._dataStore.Get<BlogEntities>(contextKey); 
     } 
    } 

    public void Dispose() 
    { 
     this._dataStore.Dispose(); 
    } 
} 

PeopleRepository(例如)應該是這樣的:

public class PeopleRepository : RepositoryBase, IPersonRepository 
{ 
    public PeopleRepository(IPerRequestDataStore dataStore) 
     : base(dataStore) 
    { 
    } 

    public Person FindById(int personId) 
    { 
     return this.Context.Persons.FirstOrDefault(p => p.PersonId == personId); 
    } 
} 

最後,這是您的創建PeopleController

IPerRequestDataStore dataStore = new StaticHttpContextDataStore(); 
UnitOfWork unitOfWork = new UnitOfWork(dataStore); 
PeopleService service = new PeopleService(unitOfWork); 
PeopleController controller = new PeopleController(service); 

這裏的一個核心概念是對象通過構造函數注入到它們的依賴關係中;這通常被認爲是很好的做法,並且更容易讓你從其他對象中構建對象。

+0

感謝史蒂夫。目前我從來沒有使用或實施過DI容器,但我絕對不排除它。但是,對於這個應用程序,我正在構建,如果我選擇不實施DI容器並保留手動DI,那麼我在我的問題中提到的代碼(特別是將UoW注入服務)仍然可以使用嗎? – tgriffiths 2012-02-03 09:23:11

+0

你當然可以手動將所有東西連接在一起;在這種情況下,你必須使用第二個選項來創建一個使用'HttpContext.Current.Items'來對每個請求存儲實體對象的對象。無論您是否使用容器,我仍然會說'UnitOfWork'應該進入服務層;我已經更新了我的答案,以澄清這一點:) – 2012-02-03 10:43:11

+0

再次感謝史蒂夫,雖然我似乎越來越困惑!你說我需要創建一個對象,它使用 HttpContext.Current.Items 來存儲我的實體在每個請求的基礎上,但我的ObjectContext將存儲/管理我的實體,它將創建 在我的單位工作類和UoW將只存在於每個HTTP請求中。 – tgriffiths 2012-02-03 16:45:32

相關問題