2016-05-29 30 views
12

In ASP.NET applicazione Nucleo posso registrare DbContext attraverso DI come questoEntity Framework Core vita servizio predefinito

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

ed è intersting di sapere qual è la sua vita?

Da qui https://github.com/aspnet/EntityFramework/blob/f33b76c0a070d08a191d67c09650f52c26e34052/src/Microsoft.EntityFrameworkCore/EntityFrameworkServiceCollectionExtensions.cs#L140 sembra configurato come Scoped che indica l'istanza di DbContext creata su ogni richiesta.

Quindi la prima parte della domanda è: È vero e se sì, allora quanto è costoso?

E la seconda parte è: Se creo un servizio che consuma DbContext e che è destinato a essere utilizzato dai controllori e avrà un'API per gestire alcune entità in DB, dovrebbe essere registrato anche come Scoped?

risposta

17

Sì, la durata predefinita per DbContext è con ambito. Questo è inteso in questo modo.

L'istante di istanza DbContext è piuttosto economico e si assicura che il proprio non usi molte risorse. Se si dispone di un valore DbContext con una durata singleton, tutti i record letti una volta verranno tracciati dallo DbContext, a meno che non si disabiliti specificamente il tracciamento. Ciò richiederà molto più utilizzo della memoria e continuerà a crescere.

E più i brani sono DbContext, minore sarà la prestazione. Questo è il motivo per cui spesso vedi DbContext utilizzato solo all'interno di un blocco using(var context = new AppDbContext()).

Nelle applicazioni Web, tuttavia, l'utilizzo del blocco using non è corretto, poiché la durata è gestita dallo framework e, se lo si smaltisce, le chiamate successive non avranno esito positivo con un'eccezione.

Se si utilizza la durata transitoria sull'altro lato, si perderà la funzionalità "transazione". Con scope, lo DbContext ha un ambito di transazione lungo quanto la richiesta.

Se è necessario un controllo più dettagliato, è necessario utilizzare il modello Unità di lavoro (che è già utilizzato da DbContext).

riguarda la seconda domanda:

Se si crea un servizio, deve avere una vita che è uguale a quella del campo di applicazione o meno lungo (leggi: con mirino o transitorio).

Se si richiede esplicitamente una durata maggiore per un servizio, è necessario immettere un servizio di fabbrica o un metodo di fabbrica DbContext nel servizio.

È possibile raggiungere questo obiettivo con qualcosa di simile

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

E il servizio potrebbe essere simile a questa:

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

in una web app è davvero il controller che gestisce tutta la vita? o è la DI che dispone di scopi nell'ambito di una richiesta alla fine della richiesta? Mi piacerebbe capire che meglio –

+0

Il mio male. Intendevo dire quadro. Esiste una factory di controller ('IControllerFactory', vedere https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Core/Controllers/DefaultControllerFactory.cs per l'implementazione predefinita) in' CreateController 'metodo che crea il controller e lo dispone e lo dispone e le sue dipendenze nel metodo' ReleaseController() '. Vedi anche questo problema su GitHub per maggiori dettagli sul funzionamento interno e perché funziona in questo modo: https://github.com/aspnet/Mvc/issues/3727 – Tseng

+0

grazie. se ho un repository che prende un DbContext nel suo costruttore e implementa IDisposable, si suppone che chiamerà dispose sul DbContext dal proprio metodo di smaltimento? Ho notato che EF UserStore per Identity non chiama dispose su dbcontext ma non mi sembra corretto, quindi sto cercando di capire perché non lo hanno fatto. –

4

Nota: In EF Core 2 ora c'è un nuovo metodo AddDbContextPool che crea una piscina di contesti che possono essere riutilizzati. L'ambito è ancora lo stesso, ma l'istanza verrà "ripristinata" e restituita al pool. Avrei pensato che il sovraccarico del "reset" sarebbe stato lo stesso di quello appena creato, ma suppongo che non sia così.

Se si utilizza questo metodo, al momento un'istanza DbContext è richiesto da un controllore per prima cosa verificare se c'è un'istanza disponibili in piscina. Una volta che l'elaborazione della richiesta finalizza, qualsiasi stato nell'istanza viene ripristinato e l'istanza è a sua volta restituite al pool. +

Questo è concettualmente simile al modo in cui il pool di connessioni opera in provider ADO.NET e ha il vantaggio di risparmiare parte del costo dell'inizializzazione dell'istanza di DbContext.

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

+0

Forse è la funzione 'OnModelCreating' che può richiedere tempo con un database di grandi dimensioni? –