2016-05-29 52 views
12

在ASP.NET的核心應用,我可以通過DI這樣實體框架的核心服務的默認壽命

services.AddDbContext<Models.ShellDbContext>(options => options.UseNpgsql(connection)); 

註冊的DbContext而且它野趣以瞭解它的生命週期是什麼?

從這裏https://github.com/aspnet/EntityFramework/blob/f33b76c0a070d08a191d67c09650f52c26e34052/src/Microsoft.EntityFrameworkCore/EntityFrameworkServiceCollectionExtensions.cs#L140它看起來像它配置爲作用域,這意味着DbContext實例是在每個請求上創建的。

所以問題的第一部分是: 這是真的,如果是的話,那麼它的代價是多少?

第二部分是: 如果我創建了一個服務,它消耗了DbContext,並且打算由控制器使用,並且將有一個API來管理數據庫中的某些實體,是否應該將其註冊爲Scoped?

回答

17

是的,DbContext的默認生存時間是有作用域的。這是打算這樣。

實例化DbContext非常便宜,它確保您不會使用很多資源。如果你有一個單身生命週期的DbContext,那麼你所讀取的所有記錄將被DbContext跟蹤,除非你明確禁用了跟蹤。這將需要更多的內存使用,並且會不斷增長。

並且越是DbContext音軌,性能將越低。這就是爲什麼你經常看到DbContext只用於using(var context = new AppDbContext())塊。

但是,在web應用程序中,使用using塊是不好的,因爲生命週期由framework管理,如果將它置於提前調用之後,將會失敗併產生異常。

如果您在另一端使用瞬態生命週期,您將失去「事務」功能。在範圍內,DbContext具有與請求一樣長的事務範圍。

如果您需要更細粒度的控制,您必須使用工作單元模式(其中DbContext已經使用)。

關於第二個問題:

如果你創建一個服務,它必須有一個生命週期這等於範圍的一個或較短(讀:作用域或瞬態)。

如果您明確需要延長服務的使用壽命,則應該將DbContext工廠服務或工廠方法注入到服務中。

你可以像

services.AddTransient<Func<AppDbContext>>((provider) => new Func<MyDbContext>(() => new AppDbContext())); 
services.AddSingleton<IMySingletonService, MySingletonService>(); 

做到這一點,您的服務可能是這樣的:

public class MySingletonService : IMySingletonService, IDisposable 
{ 
    private readonly AppDbContext context; 

    public MySingletonService(Func<AppDbContext> contextFactory) 
    { 
     if(contextFactory == null) 
      throw new ArgumentNullException(nameof(contextFactory)); 

     // it creates an transient factory, make sure to dispose it in `Dispose()` method. 
     // Since it's member of the MySingletonService, it's lifetime 
     // is effectively bound to it. 
     context = contextFactory(); 
    } 
} 
+2

這是真的,其管理生命週期控制器?或者是在請求結束時處理每個請求的作用域的DI?我想了解更好的 –

+0

我的不好。我打算說框架。有一個控制器工廠('IControllerFactory',見https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Core/Controllers/DefaultControllerFactory.cs默認實現)在它的'CreateController '創建控制器並將其部署並放置的方法,它是'ReleaseController()'方法中的依賴關係。另請參閱此GitHub問題的內部工作的更多細節,以及它爲什麼這樣工作:https://github.com/aspnet/Mvc/issues/3727 – Tseng

+0

謝謝。如果我有一個在其構造函數中使用DbContext的存儲庫,並且它實現了IDisposable,它應該從它自己的dispose方法調用DbContext上的dispose?我注意到用於Identity的EF UserStore不會在dbcontext上調用dispose,但我感覺不對,因此我試圖理解它們爲什麼不會這樣做。 –

4

注:在EF睿2現在有一種新的方法AddDbContextPool這將創建一個池可以重用的上下文。範圍仍然是相同的,但實例將「重置」並返回到池中。我會認爲'重置'的開銷與創建新的開銷是一樣的,但我想不是這樣的。

如果使用這種方法,在當時的DbContext實例請求 由控制器,我們會先檢查是否有可用 池中的實例。一旦請求處理定型,在 實例的任何狀態的復位和實例本身返回到池中。+

這在概念上類似於連接池 ADO.NET提供商如何運作,具有節約的優勢初始化DbContext實例的一些成本 。

https://docs.microsoft.com/en-us/ef/core/what-is-new/

在web應用程序
+0

猜測可能是'OnModelCreating'函數需要花費時間處理大型數據庫? –