2012-05-27 50 views
18

我對ASP.NET MVC應用程序中實體框架上下文的期望生存期有一些疑問。儘可能在最短的時間內保持活躍狀態​​不是最好的嗎?關於實體框架上下文生命週期的問題

考慮下面的控制器操作:

public ActionResult Index() 
{ 
    IEnumerable<MyTable> model; 

    using (var context = new MyEntities()) 
    { 
     model = context.MyTable; 
    } 

    return View(model); 
} 

上面的代碼將無法工作,因爲實體框架上下文超出範圍,而視圖呈現頁面。其他人如何構建上面的代碼?

+0

'model = context.MyTable.ToList()' - 'ToList()'將執行您的查詢。在你的情況下,IQueryable的確不會在上下文範圍之外工作。 – Andrei

回答

41

讓我們爭議!

我不同意一般MVC + EF共識,保持上下文,活在整個請求是有許多原因的好事:

低性能提升 你知道如何昂貴的創建新的數據庫上下文是?好...「一個DataContext是輕量級的,不貴創建」從MSDN

是獲得國際奧委會錯誤,直到你去住 如果你設置你的IoC容器來處理的,它會顯得很好..你的背景和你錯了,你真的搞錯了。我已經兩次現在看到從IoC容器創建的大量內存泄漏並不總是正確地處理上下文。直到您的服務器在正常級別的併發用戶中開始崩潰時,您纔會意識到您已將其設置爲錯誤。它不會發生在開發中,所以做一些負載測試!

意外延遲加載 您返回最新文章的IQueryable,以便您可以在主頁上列出它們。有一天,其他人被要求顯示相應文章旁邊的評論數量。因此,他們的代碼添加一個簡單的位到View顯示,像這樣的評論了......

@foreach(var article in Model.Articles) { 
    <div> 
     <b>@article.Title</b> <span>@article.Comments.Count() comments</span> 
    </div> 
} 

外觀精緻,做工精細。但實際上,您並未在返回的數據中包含註釋,因此現在將爲循環中的每篇文章創建一個新的數據庫調用。選擇N + 1問題。 10篇文章= 11個數據庫調用。好,所以代碼是錯誤的,但這是一個容易犯的錯誤,所以它會發生。

您可以通過在數據層中關閉您的上下文來防止此問題。但是不會在article.Comments.Count()上的NullReferenceException中破解代碼嗎?是的,它會迫使你編輯圖層以獲取視圖圖層所需的數據。這是應該如何。

代碼異味 從您的視圖中擊中數據庫只是錯誤的。你知道一個IQueryable實際上並沒有實際打到數據庫,所以忘了這個對象。確保你的數據庫在離開你的數據層之前被擊中。

所以答案

您的代碼應該是這樣的

數據層(在我看來):

public List<Article> GetArticles() 
{ 
    List<Article> model; 

    using (var context = new MyEntities()) 
    { 
     //for an example I've assumed your "MyTable" is a table of news articles 
     model = (from mt in context.Articles 
       select mt).ToList(); 
     //data in a List<T> so the database has been hit now and data is final 
    } 

    return model; 
} 

控制器:

public ActionResult Index() 
{ 
    var model = new HomeViewModel(); //class with the bits needed for you view 
    model.Articles = _dataservice.GetArticles(); //irrelevant how _dataService was intialised 
    return View(model); 
} 

一旦你這樣做這並理解這可能喲你可以開始試驗擁有一個IoC容器句柄的上下文,但絕對不是之前。頭部我的警告 - 我已經看到兩個大規模失敗:)

但老實說,你喜歡什麼,編程很有趣,應該是一個偏好問題。我只是告訴你我的。但是無論你做什麼,都不要開始使用每個控制器或每個請求的IoC上下文,因爲「所有酷的孩子都在這樣做。」這樣做是因爲你真的關心它的好處,並瞭解它是如何正確完成的。

+3

我認爲值得從MSDN引用整個段落:「一般來說,DataContext實例的設計目的是爲了保持一個'工作單元',然而你的應用程序定義了這個術語,DataContext是輕量級的,創建起來並不昂貴。 LINQ to SQL應用程序在方法範圍創建DataContext實例,或者作爲表示相關數據庫操作的邏輯集合的短期類的成員。「 –

+1

@BritishDeveloper:問題是關於來自System.Data.Entity命名空間(實體框架dbcontext類)的DbContext。在[MSDN](http://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext%28v=vs.113%29)中描述此上下文時,我沒有發現任何「輕量級」。 ASPX)。你有其他證據嗎? – vk5880

+0

啊,這是2年前,cba爲你找到更多的證據。但是,在過去的幾年中,我在大網站上使用這種方法的確有專業經驗,沒有出現任何困難或性能問題。 – BritishDeveloper

2

首先,你應該考慮把你的數據庫訪問分爲不同的類。其次,我最喜歡的解決方案是使用「每個請求一個上下文」(如果您使用的是MVC,我相信它是每個控制器的一個上下文)。

要求編輯:

看一看這個答案,也許它會幫助你。請注意我正在使用webforms,所以目前無法在MVC中驗證它,但它可能對您有所幫助,或者至少會給您一些指示。 https://stackoverflow.com/a/10153406/1289283

本的DbContext的一些用法示例:

public class SomeDataAccessClass 
{ 
    public static IQueryable<Product> GetAllProducts() 
    { 
     var products = from o in ContextPerRequest.Current.Products 
         select o; 
     return products; 
    } 
} 

然後,你可以做這樣的事情:

public ActionResult Index() 
{ 
    var products = SomeDataAccessClass.GetProducts(); 
    return View(products); 
} 

簡單,對不對?你不必擔心處理你的上下文,只寫你真正需要的代碼。

有些人喜歡通過添加UnitOfWork模式或者IoC容器來進一步調味東西......但是我更喜歡這種方法,因爲它的簡單性。

+0

謝謝,但我想弄清楚人們會如何構造代碼,以便每個請求只有一個上下文?我會在哪裏創建它,以及在請求完成後如何確保及時處理它? –

5

我與每個請求一個方面同意,我們通常通過結合使用Ninject,它的作品真的很好,上下文.InRequestScope做到這一點:

Bind<MyContext>().ToSelf().InRequestScope(); 

也是其枚舉儘可能接近真正的好做法以儘可能查詢,即:

public ActionResult Index() 
{ 
    IEnumerable<MyTable> model; 

    using (var context = new MyEntities()) 
    { 
     model = (from mt in context.MyTable 
       select mt).ToArray(); 
    } 
    return View(model); 
} 

這將幫助您避免在您的視圖中無意中增加查詢。

1

你可以使用LINQ的.ToList()擴展方法這樣:

public ActionResult Index() 
{ 
    IEnumerable<MyTable> model; 

    using (var context = new MyEntities()) 
    { 
     model = (from mt in context.MyTable 
       select mt).ToList(); 
    } 
    return View(model); 
} 
+0

是的,我可以,但這並沒有解決在單個HTTP請求期間多次重新創建上下文的潛在性能問題。這就是爲什麼我正在尋找其他人如何做這件事的例子。 –

+1

您是否測量過性能影響?它是否符合您的期望/要求? –

+0

我還沒有測量性能。我只是想弄清楚別人是如何處理EF的這個特定方面的。我很難找到很多例子。 –