Nell'applicazione ASP.NET Core posso registrare DbContext tramite DI in questo modo
services.AddDbContext<Models.ShellDbContext>(options => options.UseNpgsql(connection));
Ed è interessante sapere qual è la sua vita?
Da qui https://github.com/aspnet/EntityFramework/blob/f33b76c0a070d08a191d67c09650f52c26e34052/src/Microsoft.EntityFrameworkCore/EntityFrameworkServiceCollectionExtensions.cs#L140 sembra configurato come Scoped, ovvero l'istanza di DbContext viene creata su ogni richiesta.
Quindi la prima parte della domanda è: è vero e se sì, allora quanto costa?
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?
Sì, l'intervallo di tempo predefinito per DbContext
è DbContext
. Questo è inteso in questo modo.
L'istanziazione di DbContext
è piuttosto economica e fa in modo che il tuo non usi molte risorse. Se si dispone di un DbContext
con una durata singleton, tutti i record letti una volta verranno tracciati da DbContext
, a meno che non si disabiliti specificamente il tracciamento. Ciò richiederà molto più utilizzo della memoria e continuerà a crescere.
E più tracce di DbContext
, minore sarà la performance. Ecco perché spesso vedi DbContext
utilizzato solo all'interno di un blocco using(var context = new AppDbContext())
.
Nelle applicazioni Web, tuttavia, l' using
blocco using
è errato, poiché la durata è gestita dal framework e, se la si elimina prima, le chiamate successive falliscono con un'eccezione.
Se si utilizza la durata transitoria sull'altro lato, si perderà la funzionalità "transazione". Con ambito, DbContext
ha un ambito di transazione lungo quanto la richiesta.
Se è necessario un controllo più dettagliato, è necessario utilizzare il modello Unità di lavoro (che DbContext
già utilizza).
Per la tua seconda domanda:
Se crei un servizio, deve avere una durata pari a quella dell'ambito o inferiore (leggi: Scoped o transient).
Se si richiede esplicitamente una vita più lunga per un servizio, è necessario DbContext
un servizio di fabbrica DbContext
o un metodo di fabbrica nel proprio servizio.
Puoi farlo con qualcosa di simile
services.AddTransient<Func<AppDbContext>>( (provider) => new Func<MyDbContext>( () => new AppDbContext()));
services.AddSingleton<IMySingletonService, MySingletonService>();
E il tuo servizio potrebbe assomigliare a questo:
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();
}
}
Nota: in EF Core 2 è ora disponibile un nuovo metodo AddDbContextPool
che crea un pool di contesti che possono essere riutilizzati. L'ambito è sempre lo stesso, ma l'istanza verrà "ripristinata" e restituita al pool. Avrei pensato che il sovraccarico del "reset" sarebbe stato lo stesso del crearne uno nuovo, ma credo che non sia così.
Se viene utilizzato questo metodo, nel momento in cui un'istanza DbContext viene richiesta da un controller, verificheremo innanzitutto se nel pool è disponibile un'istanza. Una volta finalizzata l'elaborazione della richiesta, viene ripristinato qualsiasi stato sull'istanza e l'istanza viene restituita al pool. +
Ciò è concettualmente simile al funzionamento del pool di connessioni nei provider ADO.NET e presenta il vantaggio di risparmiare parte del costo dell'inizializzazione dell'istanza DbContext.
https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.0#high-performance