tl; dr Come posso usare Entity Framework in un'applicazione API .NET Core multithread anche se DbContext non è thread-safe?
Sto lavorando su un'app API .NET Core che espone diverse interfacce RESTful che accedono al database e leggono i dati da esso, mentre allo stesso tempo eseguo diversi TimedHostedServices come thread di lavoro in background che eseguono il polling periodico dei dati da altri servizi Web e li memorizzano nel database.
Sono consapevole del fatto che DbContext non è thread-safe. Ho letto molti documenti, post di blog e risposte qui su StackOverflow e ho potuto trovare molte risposte (in parte contraddittorie) per questo, ma nessuna vera "migliore pratica" quando lavoravo anche con DI.
Utilizzando il default ServiceLifetime.Scoped
tramite i AddDbContext
metodo di estensione risultati in eccezioni dovute a condizioni di gara.
Non voglio lavorare con i lucchetti (ad esempio Semaphore), poiché gli aspetti negativi ovvi sono:
Non iniettare MyDbContext
ma DbContextOptions<MyDbContext>
invece, costruendo il contesto solo quando ho bisogno di accedere al db, using
un'istruzione using
per smaltirlo immediatamente dopo la lettura / scrittura sembra un sacco di sovraccarico di utilizzo delle risorse e inutilmente molte aperture / chiusure della connessione .
Sono davvero perplesso: come può essere raggiunto?
Non credo che il mio caso d'uso sia super speciale - popolando il db da un lavoratore in background e interrogandolo dal livello API Web - quindi dovrebbe esserci un modo significativo di farlo con ef core.
Molte grazie!
È necessario creare un ambito ogni volta che si attiva TimedHostedServices
.
Iniettare il fornitore di servizi nel costruttore:
public MyServiceService(IServiceProvider services)
{
_services = services;
}
e quindi creare un ambito ogni volta che si attiva l'attività
using (var scope = _services.CreateScope())
{
var anotherService = scope.ServiceProvider.GetRequiredService<AnotherService>();
anotherService.Something();
}
Un esempio più completo è disponibile nel documento
Un altro approccio per creare il proprio DbContextFactory e creare un'istanza di nuova istanza per ogni query.
public class DbContextFactory
{
public YourDbContext Create()
{
var options = new DbContextOptionsBuilder<YourDbContext>()
.UseSqlServer(_connectionString)
.Options;
return new YourDbContext(options);
}
}
uso
public class Service
{
private readonly DbContextFactory _dbContextFactory;
public Service(DbContextFactory dbContextFactory)
=> _dbContextFactory = dbContextFactory;
public void Execute()
{
using (var context = _dbContextFactory.Create())
{
// use context
}
}
}
Con factory non devi più preoccuparti degli ambiti e rendere il tuo codice libero dalle dipendenze ASP.NET Core.
Sarai in grado di eseguire query in modo asincrono, cosa impossibile con DbContext con ambito senza soluzioni alternative.
Sei sempre sicuro di quali dati sono stati salvati quando chiami .SaveChanges()
, dove con DbContext con ambito ci sono possibilità che alcune entità siano state cambiate in un'altra classe.