2010-12-18 35 views
31

我工作的一個ASP.net MVC應用程序,我有一個關於使用構造我的控制器的問題。ASP.net MVC控制器 - 構造函數用法

我使用實體框架和LINQ到實體爲我所有的數據交易。我需要訪問我的實體模型幾乎所有的控制器操作。當我第一次開始編寫應用程序時,我在每個Action方法的開頭創建一個實體對象,執行我需要的任何工作,然後返回結果。

我意識到,我是一遍又一遍地創建相同的對象的每個操作方法所以我創建針對該實體對象私有成員變量,並開始在構造每個控制器實例化它。現在每個方法只引用該私有成員變量來完成它的工作。

我還在質疑自己上哪條路是正確的。我想知道A.)哪種方法最合適? B.)在構造方法中,這些對象有多久生活? C.)構造函數方法是否存在性能/完整性問題?

謝謝

+0

只有一個控制器需要該對象嗎? – 2010-12-18 22:36:06

+0

可能你應該使用單例模式與私有靜態實例和延遲加載get實例的屬性。 – 2010-12-18 22:39:38

回答

26

你在問正確的問題。

A.這肯定是不恰當的創建這個每個操作方法中的依賴關係。 MVC的主要特點之一是能夠區分顧慮。通過加載你的控制器與這些依賴關係,你使控制器變得厚重。這些應該注入控制器。依賴注入(DI)有多種選擇。通常,這些類型的對象可以注入構造函數或屬性中。我的首選是構造函數注入。

B.這些對象的壽命將被垃圾收集器來確定。 GC不是確定性的。所以,如果你的對象有資源受限的服務(數據庫連接)的連接,那麼你可能需要確定你關閉了你自己的連接(而不是依賴於配置)。很多時候,「終生」問題被分解爲控制反轉(IOC)容器。那裏有很多。我的首選是Ninject。 C.實例化成本可能是最小的。數據庫事務成本是您可能希望關注的地方。有一個概念叫做'工作單位',你可能想看看。本質上,數據庫可以處理大於一次保存/更新操作的事務。增加事務大小可以提高數據庫性能。

希望能讓你開始。

鮑勃

+1

嘗試NINJECT依賴注入! – Omkar 2010-12-18 22:54:31

+1

「確保你關閉你自己的連接(而不是依靠處置)」 - 我強烈反對。一次性控制器由ControllerFactory處理,而不是由GC處理,其處置是確定性的。有關詳細信息,請參閱[此問題](http://stackoverflow.com/questions/1380019/asp-mvc-when-is-icontroller-dispose-called)。 – Alex 2015-09-18 13:28:21

28

RCravens有一些很好的見解。我想展示如何實施他的建議。

這將是很好定義的接口,爲數據訪問類來實現啓動:

public interface IPostRepository 
{ 
    IEnumerable<Post> GetMostRecentPosts(int blogId); 
} 

然後實現數據類。實體框架上下文構建起來很便宜,並且在不處理它們時可能會出現不一致的行爲,所以我發現將內存中的數據提取出來然後處理上下文通常會更好。

public class PostRepository : IPostRepository 
{ 
    public IEnumerable<Post> GetMostRecentPosts(int blogId) 
    { 
     // A using statement makes sure the context is disposed quickly. 
     using(var context = new BlogContext()) 
     { 
      return context.Posts 
       .Where(p => p.UserId == userId) 
       .OrderByDescending(p => p.TimeStamp) 
       .Take(10) 
       // ToList ensures the values are in memory before disposing the context 
       .ToList(); 
     } 
    } 
} 

現在你的控制器能夠接受這些庫作爲構造函數的參數之一:

public class BlogController : Controller 
{ 
    private IPostRepository _postRepository; 
    public BlogController(IPostRepository postRepository) 
    { 
     _postRepository = postRepository; 
    } 

    public ActionResult Index(int blogId) 
    { 
     var posts = _postRepository.GetMostRecentPosts(blogId); 
     var model = new PostsModel { Posts = posts }; 
     if(!posts.Any()) {model.Message = "This blog doesn't have any posts yet";} 
     return View("Posts", model); 
    } 

} 

MVC可以讓你用你自己的控制器廠代替默認的,所以你可以指定你的IoC像Ninject這樣的框架決定了如何創建控制器。你可以設置你的注入框架,知道當你要求一個IPostRepository時,它應該創建一個PostRepository對象。

這種方法的一大優勢是它使您的控制器可以進行單元測試。例如,如果你想確保你的模型獲取消息時,有沒有文章,你可以像使用最小起訂量嘲弄的框架,建立這樣一個場景,你的資料庫沒有返回崗位:

var repositoryMock = new Mock<IPostRepository>(); 
repositoryMock.Setup(r => r.GetMostRecentPosts(1)) 
    .Returns(Enumerable.Empty<Post>()); 
var controller = new BlogController(repositoryMock.Object); 
var result = (ViewResult)controller.Index(1); 
Assert.IsFalse(string.IsNullOrEmpty(result.Model.Message)); 

這使得很容易測試您對控制器操作期望的特定行爲,而無需設置數據庫或其他任何特殊的東西。像這樣的單元測試很容易編寫,具有確定性(它們的通過/失敗狀態基於代碼,而不是數據庫內容),並且速度很快(一秒鐘可以運行一千次)。

+0

太好了。謝謝您的幫助。在我的腦海中,我有一個類似的模型(DI),但我仍然在學習MVC的細節。我認爲是時候回去爲可持續發展的控制器打造了。非常感謝。 – BZink 2010-12-20 19:15:50

+2

你會如何發送一個參數給構造函數?我這樣做的方式相同,除了在構造函數中,我只有'myRepository = new MyRepository()'。 – Cody 2013-06-18 21:29:58

+0

@DoctorOreo:正如我在帖子中所說的,MVC允許你指定一個控制器工廠。您可以創建一個控制器工廠,該工廠在創建控制器時知道要傳入存儲庫。或者您可以使用現有的Dependency-injection框架,設置存儲庫的綁定,並讓MVC請求DI框架創建控制器。 – StriplingWarrior 2013-06-18 22:00:23