Megosztás:


DbContext élettartama, konfigurálása és inicializálása

Ez a cikk a DbContext példány inicializálásának és konfigurálásának alapvető mintáit ismerteti.

Figyelmeztetés

Ez a cikk egy helyi adatbázist használ, amely nem igényli a felhasználó hitelesítését. A termelésben használt alkalmazásoknak az elérhető legbiztonságosabb hitelesítési folyamatot kell használniuk. Az üzembe helyezett teszt- és éles alkalmazások hitelesítéséről további információt Biztonságos hitelesítési folyamatokcímű témakörben talál.

A DbContext élettartama

Egy példány élettartama DbContext a példány létrehozásakor kezdődik, és a példány megsemmisítésekor ér véget. A DbContext példányok egyetlenmunkaegységhez használhatók. Ez azt jelenti, hogy egy DbContext példány élettartama általában nagyon rövid.

Jótanács

Martin Fowlert a fenti hivatkozásból idézve: "A Munkaegység nyomon követi az adatbázist érintő üzleti tranzakciók során végrehajtott műveleteket. Ha végzett, az mindent kitalál, amit meg kell tenni az adatbázis módosításához a munkája eredményeként."

Az Entity Framework Core (EF Core) használatakor általában a következőket kell elvégezni:

Fontos

  • Fontos a használat után megsemmisíteni.DbContext Ez biztosítja a következőket:
    • A nem felügyelt erőforrások felszabadulnak.
    • Az események vagy egyéb horgok regisztrálását megszüntetik. A regisztráció törlése megakadályozza a memóriavesztést, ha a példányra továbbra is hivatkoznak.
  • A DbContext nem szálbiztos. Ne ossza meg a környezeteket a szálak között. Mielőtt folytatná a környezeti példány használatát, mindenképpen várja meg az összes aszinkron hívást.
  • Az InvalidOperationException, amelyet az EF Core kód dob, olyan állapotba hozhatja a környezetet, amelyből nem lehet helyreállni. Az ilyen kivételek programhibát jeleznek, és nem arra szolgálnak, hogy helyrehozhatók legyenek.

DbContext a ASP.NET Core függőséginjektálásában

Számos webalkalmazásban minden HTTP-kérés egyetlen munkaegységnek felel meg. Így a környezet élettartamának a kéréshez való csatlakoztatása jó alapértelmezett érték a webalkalmazások számára.

ASP.NET Alapalkalmazások függőséginjektálással vannak konfigurálva. Az EF Core hozzáadható ehhez a konfigurációhoz a(z) AddDbContext és Program.cs segítségével. Például:

var connectionString =
    builder.Configuration.GetConnectionString("DefaultConnection")
        ?? throw new InvalidOperationException("Connection string"
        + "'DefaultConnection' not found.");

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));

Az előző kód regisztrálja a ApplicationDbContext nevű alosztályt, amely a DbContext alosztálya, mint hatókörrel rendelkező szolgáltatást az ASP.NET Core szolgáltatói rendszerben. A szolgáltatót függőséginjektálási tárolónak is nevezik. A környezet úgy van konfigurálva, hogy az SQL Server-adatbázis-szolgáltatót használja, és beolvassa a kapcsolati sztringet ASP.NET Core-konfigurációból.

Az ApplicationDbContext osztálynak közzé kell tennie egy nyilvános konstruktort egy DbContextOptions<ApplicationDbContext> paraméterrel. Így kerül továbbításra a környezetkonfiguráció AddDbContext a DbContext számára. Például:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

ApplicationDbContext konstruktorinjektálással használható ASP.NET Core vezérlőkben vagy más szolgáltatásokban:

public class MyController
{
    private readonly ApplicationDbContext _context;

    public MyController(ApplicationDbContext context)
    {
        _context = context;
    }
}

A végeredmény egy ApplicationDbContext példány, amely minden kéréshez létrejön, majd a vezérlőhöz kerül, hogy elvégezze a munkaegységet, mielőtt a kérés befejeződésével megsemmisítésre kerül.

A konfigurációs beállításokról további információt ebben a cikkben talál. További információ: Függőséginjektálás a ASP.NET Core-ban .

Alapszintű DbContext-inicializálás az "új" szöveggel

A DbContext példányok C#-ban new hozhatók létre. A konfiguráció a OnConfiguring metódus felülírásával vagy opciók konstruktorba való átadásával hajtható végre. Például:

public class ApplicationDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(
            @"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
    }
}

Ez a minta megkönnyíti a konfiguráció, például a kapcsolati sztring átadását a DbContext konstruktoron keresztül. Például:

public class ApplicationDbContext : DbContext
{
    private readonly string _connectionString;

    public ApplicationDbContext(string connectionString)
    {
        _connectionString = connectionString;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(_connectionString);
    }
}

Másik lehetőségként a DbContextOptionsBuilder segítségével létrehozhat egy DbContextOptions objektumot, amelyet aztán átad a DbContext konstruktornak. Ez lehetővé teszi, hogy a DbContext függőséginjektálásra konfigurált elemeket explicit módon is létrehozzuk. Ha például a fenti ASP.NET Core-webalkalmazásokhoz van definiálva ApplicationDbContext :

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

A DbContextOptions létrehozható, és a konstruktor explicit módon meghívható.

var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
    .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0")
    .Options;

using var context = new ApplicationDbContext(contextOptions);

DbContext-előállító használata

Egyes alkalmazástípusok (például ASP.NET Core Blazor) függőséginjektálást használnak, de nem hoznak létre olyan szolgáltatáshatókört, amely megfelel a kívánt DbContext élettartamnak. Még akkor is, ha létezik ilyen igazítás, előfordulhat, hogy az alkalmazásnak több munkaegységet kell végrehajtania ezen a hatókörön belül. Például több munkaegység egyetlen HTTP-kérelemben.

Ezekben az esetekben AddDbContextFactory használható egy gyár regisztrálásához DbContext példányok létrehozása érdekében. Például:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContextFactory<ApplicationDbContext>(
        options => options.UseSqlServer(
            @"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0"));
}

Az ApplicationDbContext osztálynak közzé kell tennie egy nyilvános konstruktort egy DbContextOptions<ApplicationDbContext> paraméterrel. Ez ugyanaz a minta, mint a fenti hagyományos ASP.NET Core szakaszban.

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

A DbContextFactory gyár ezután konstruktorinjektálással más szolgáltatásokban is használható. Például:

private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;

public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
    _contextFactory = contextFactory;
}

Az injektált gyár ezután a dbContext-példányok szolgáltatáskódban való létrehozásához használható. Például:

public async Task DoSomething()
{
    using (var context = _contextFactory.CreateDbContext())
    {
        // ...
    }
}

Figyelje meg, hogy az DbContext így létrehozott példányokat nem az alkalmazás szolgáltatója kezeli, ezért az alkalmazásnak kell megsemmisítenie.

Az EF Core és a Blazor használatával kapcsolatos további információkért tekintse meg ASP.NET Core Blazor-kiszolgálót az Entity Framework Core használatával.

DbContextOptions

Az összes DbContext konfiguráció kiindulópontja a DbContextOptionsBuilder. A builder beszerzésének három módja van:

  • In AddDbContext és a kapcsolódó módszerek
  • Ban OnConfiguring
  • Kifejezetten a new

Ezekre példák az előző szakaszokban láthatók. Ugyanez a konfiguráció alkalmazható függetlenül attól, hogy honnan származik a szerkesztő. Emellett OnConfiguring mindig a környezet felépítésétől függetlenül hívjuk. Ez azt jelenti OnConfiguring , hogy további konfigurációk végrehajtására is használható, még akkor is, ha AddDbContext használatban van.

Az adatbázis-szolgáltató konfigurálása

Minden DbContext példányt úgy kell konfigurálni, hogy csak egy adatbázis-szolgáltatót használjon. (Az altípus különböző példányai DbContext különböző adatbázis-szolgáltatókkal használhatók, de egyetlen példánynak csak egyet kell használnia.) Az adatbázis-szolgáltató egy adott Use* hívással van konfigurálva. Például az SQL Server adatbázis-szolgáltatójának használata:

public class ApplicationDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(
            @"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
    }
}

Ezek a Use* metódusok az adatbázis-szolgáltató által implementált bővítménymetszetek. Ez azt jelenti, hogy az adatbázis-szolgáltató NuGet-csomagjának telepítve kell lennie a bővítménymetódus használata előtt.

Jótanács

Az EF Core-adatbázis-szolgáltatók széles körben használják a bővítménymetelyeket. Ha a fordító azt jelzi, hogy nem található metódus, győződjön meg arról, hogy a szolgáltató NuGet-csomagja telepítve van, és hogy szerepel-e using Microsoft.EntityFrameworkCore; a kódban.

Az alábbi táblázat példákat tartalmaz a gyakori adatbázis-szolgáltatókra.

Adatbázis-rendszer Konfigurációs példa NuGet-csomag
SQL Server vagy Azure SQL .UseSqlServer(connectionString) Microsoft.EntityFrameworkCore.SqlServer
Azure Cosmos DB .UseCosmos(connectionString, databaseName) Microsoft.EntityFrameworkCore.Cosmos
SQLite .UseSqlite(connectionString) Microsoft.EntityFrameworkCore.Sqlite
EF Core memóriabeli adatbázis .UseInMemoryDatabase(databaseName) Microsoft.EntityFrameworkCore.InMemory
PostgreSQL* .UseNpgsql(connectionString) Npgsql.EntityFrameworkCore.PostgreSQL
MySQL/MariaDB* .UseMySql(connectionString) Pomelo.EntityFrameworkCore.MySql
Oracle* .UseOracle(connectionString) Oracle.EntityFrameworkCore

*Ezeket az adatbázis-szolgáltatókat a Microsoft nem szállítja. Az adatbázis-szolgáltatókkal kapcsolatos további információkért tekintse meg az adatbázis-szolgáltatókat.

Figyelmeztetés

Az EF Core memórián belüli adatbázisa nem éles használatra lett tervezve. Emellett nem biztos, hogy ez a legjobb választás még a teszteléshez sem. További információkért lásd az EF Core-t használó tesztelési kódot .

A Kapcsolati sztringek EF Core-val való használatáról további információkat a Kapcsolati sztringek című témakörben találhat.

Az adatbázis-szolgáltatóra vonatkozó opcionális konfigurációt egy további szolgáltatóspecifikus szerkesztő hajtja végre. Például az Azure SQL-hez való csatlakozáskor az újrapróbálkozások beállításával javítható a kapcsolat rugalmassága:

public class ApplicationDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer(
                @"Server=(localdb)\mssqllocaldb;Database=Test",
                providerOptions => { providerOptions.EnableRetryOnFailure(); });
    }
}

Jótanács

Ugyanazt az adatbázis-szolgáltatót használja az SQL Server és az Azure SQL. Az SQL Azure-hoz való csatlakozáskor azonban ajánlott a kapcsolat rugalmasságát használni.

A szolgáltatóspecifikus konfigurációval kapcsolatos további információkért tekintse meg az adatbázis-szolgáltatókat .

Egyéb DbContext-konfiguráció

Más DbContext konfigurációk akár a Use* hívás előtt, akár után láncolhatók, nincs jelentősége, hogy melyik. Például a bizalmas adatok naplózásának bekapcsolása:

public class ApplicationDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .EnableSensitiveDataLogging()
            .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
    }
}

Az alábbi táblázat példákat tartalmaz a gyakori metódusokra.DbContextOptionsBuilder

DbContextOptionsBuilder függvény A teendők Tudj meg többet
UseQueryTrackingBehavior A lekérdezések alapértelmezett nyomkövetési viselkedésének beállítása Lekérdezéskövetési viselkedés
LogTo Az EF Core-naplók egyszerű lekérésének módja Naplózás, események és diagnosztika
UseLoggerFactory Egy Microsoft.Extensions.Logging gyártelep regisztrálása Naplózás, események és diagnosztika
EnableSensitiveDataLogging Alkalmazásadatokat tartalmaz a kivételekben és a naplózásban Naplózás, események és diagnosztika
EnableDetailedErrors Részletesebb lekérdezési hibák (a teljesítmény rovására) Naplózás, események és diagnosztika
ConfigureWarnings Figyelmeztetések és egyéb események figyelmen kívül hagyása vagy elvetése Naplózás, események és diagnosztika
AddInterceptors EF Core elfogók regisztrálása Naplózás, események és diagnosztika
EnableServiceProviderCaching A belső szolgáltató gyorsítótárazásának szabályozása Szolgáltató cache-elése
UseMemoryCache Az EF Core által használt memória-gyorsítótár konfigurálása Memóriagyorsítótár-integráció
UseLazyLoadingProxies Dinamikus proxyk használata lusta betöltéshez Halasztott betöltés
UseChangeTrackingProxies Dinamikus proxyk használata a változáskövetéshez Hamarosan...

Megjegyzés:

UseLazyLoadingProxies és UseChangeTrackingProxies a Microsoft.EntityFrameworkCore.Proxies NuGet-csomag bővítménymetszetei. Ez a fajta ". A UseSomething()" hívás a más csomagokban található EF Core-bővítmények konfigurálásának és/vagy használatának ajánlott módja.

DbContextOptions és DbContextOptions<TContext>

Az a legtöbb DbContext alosztálynak, amely elfogadja az általános DbContextOptions változatot, az általánosDbContextOptions<TContext> változatot kell használnia. Például:

public sealed class SealedApplicationDbContext : DbContext
{
    public SealedApplicationDbContext(DbContextOptions<SealedApplicationDbContext> contextOptions)
        : base(contextOptions)
    {
    }
}

Ez biztosítja, hogy az adott DbContext altípus megfelelő beállításai feloldódtak a függőséginjektálással, még akkor is, ha több DbContext altípus van regisztrálva.

Jótanács

A DbContext-nek nem kell zártnak lennie, de a zárttá tétel ajánlott gyakorlat az olyan osztályok esetében, amelyeket nem öröklésre terveztek.

Ha azonban az DbContext altípus maga is öröklendő, akkor egy nem általános DbContextOptionsértéket használó védett konstruktort kell elérhetővé tennie. Például:

public abstract class ApplicationDbContextBase : DbContext
{
    protected ApplicationDbContextBase(DbContextOptions contextOptions)
        : base(contextOptions)
    {
    }
}

Így több konkrét alosztály is meghívhatja ezt az alapkonstruktort a különböző általános DbContextOptions<TContext> példányaik használatával. Például:

public sealed class ApplicationDbContext1 : ApplicationDbContextBase
{
    public ApplicationDbContext1(DbContextOptions<ApplicationDbContext1> contextOptions)
        : base(contextOptions)
    {
    }
}

public sealed class ApplicationDbContext2 : ApplicationDbContextBase
{
    public ApplicationDbContext2(DbContextOptions<ApplicationDbContext2> contextOptions)
        : base(contextOptions)
    {
    }
}

Észre fogja venni, hogy ez ugyanolyan minta, mint amikor közvetlenül a DbContext öröklünk. A DbContext konstruktor maga is elfogad egy nem generikus DbContextOptions, és ezt indokolja.

A DbContext példányosított és öröklődő alosztálynak a konstruktor mindkét formáját elérhetővé kell tennie. Például:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> contextOptions)
        : base(contextOptions)
    {
    }

    protected ApplicationDbContext(DbContextOptions contextOptions)
        : base(contextOptions)
    {
    }
}

Tervezési idejű DbContext-konfiguráció

Az EF Core tervezési idő eszközeinek, például az EF Core-migrálásokhoz , képesnek kell lenniük egy működő típusú példány DbContext felderítésére és létrehozására annak érdekében, hogy adatokat gyűjthessenek az alkalmazás entitástípusairól és arról, hogyan képezhetők le egy adatbázissémára. Ez a folyamat automatikus lehet, amíg az eszköz egyszerűen létrehozhatja azt DbContext úgy, hogy a futásidőben konfigurálthoz hasonlóan legyen konfigurálva.

Bár minden olyan minta, amely biztosítja a szükséges konfigurációs információkat a DbContext futtatáskor működik, azok az eszközök, amelyek tervezési időben megkövetelik a DbContext használatát, csak korlátozott számú mintával kompatibilisek. Ezekről részletesebben Design-Time Környezet létrehozása című témakörben olvashat.

A DbContext-szálkezeléssel kapcsolatos problémák elkerülése

Az Entity Framework Core nem támogatja, hogy több párhuzamos művelet fusson ugyanazon DbContext a példányon. Ez magában foglalja az aszinkron lekérdezések párhuzamos végrehajtását és a több szálból származó explicit egyidejű használatot is. Ezért mindig await azonnal aszinkron hívásokat kezdeményez, vagy használjon külön DbContext példányokat a párhuzamosan futó műveletekhez.

Ha az EF Core egyidejűleg észlel egy DbContext példány használatára tett kísérletet, a következőhöz hasonló üzenet jelenik meg InvalidOperationException :

Egy második művelet ezen a környezetben kezdődött, mielőtt egy korábbi művelet befejeződött. Ezt általában különböző szálak okozzák, amelyek ugyanazt a DbContext-példányt használják, azonban a példánytagok önmagukban nem garantálják a szálbiztosságot.

Ha az egyidejű hozzáférés nem látható, az meghatározatlan viselkedést, az alkalmazás összeomlását és adatsérülést eredményezhet.

Vannak gyakori hibák, amelyek véletlenül egyidejű hozzáférést okozhatnak ugyanazon DbContext a példányon:

Aszinkron művelet buktatói

Az aszinkron metódusok lehetővé teszik, hogy az EF Core olyan műveleteket kezdeményezhessen, amelyek nem blokkoló módon férnek hozzá az adatbázishoz. De ha egy hívó nem várja meg az egyik metódus befejezését, és más műveleteket hajt végre a DbContext-n, akkor a DbContext állapota megsérülhet (és nagyon valószínű, hogy meg is sérül).

Mindig azonnal várja meg az EF Core aszinkron metódusokat.

DbContext-példányok implicit megosztása függőséginjektálással

A AddDbContext bővítménymetódus alapértelmezés szerint DbContext regisztrálja a típusokat.

Ez biztonságos a legtöbb ASP.NET Core-alkalmazás egyidejű hozzáférési problémáitól, mivel egy adott időpontban csak egy szál hajtja végre az egyes ügyfélkéréseket, és mivel minden kérés külön függőséginjektálási hatókört kap (és ezért külön DbContext példányt). A Blazor Server üzemeltetési modell esetében a Rendszer egyetlen logikai kérést használ a Blazor felhasználói kapcsolatcsoport fenntartásához, így felhasználói kapcsolatcsoportonként csak egy hatókörű DbContext-példány érhető el, ha az alapértelmezett injektálási hatókört használja.

Minden olyan kódnak, amely több szálat futtat párhuzamosan, biztosítania kell, hogy DbContext a példányok soha ne legyenek egyidejűleg elérhetők.

A függőséginjektálás használata során ezt úgy érheti el, hogy a kontextust scoped-ként regisztrálja, és minden egyes szálhoz hatóköröket hoz létre (IServiceScopeFactory használatával), vagy a DbContext-t transientként regisztrálja (a AddDbContext túlterhelt változatával, amely egy ServiceLifetime paramétert vesz igénybe).

ConfigureDbContext konfiguráció összeállításához

Megjegyzés:

Ez a szakasz az EF Core elsődlegesen újrafelhasználható kódtárakhoz és összetevőkhöz készült köztes szintű használatát ismerteti. A legtöbb alkalmazásnak a AddDbContextFactory cikkben korábban ismertetett mintát kell használnia.

Az EF Core 9.0-tól kezdve további konfigurációkat alkalmazhat a DbContext elemre a AddDbContext hívás előtt vagy után a ConfigureDbContext segítségével. Ez különösen hasznos az újrahasználható összetevőkben vagy tesztekben nem ütköző konfigurációk írásához.

Alapszintű ConfigureDbContext-használat

ConfigureDbContext Lehetővé teszi, hogy a teljes szolgáltatói konfiguráció cseréje nélkül adjon hozzá konfigurációt egy újrafelhasználható tárhoz vagy összetevőhöz:

var services = new ServiceCollection();

services.ConfigureDbContext<BlogContext>(options =>
    options.EnableSensitiveDataLogging()
           .EnableDetailedErrors());

services.AddDbContext<BlogContext>(options =>
    options.UseInMemoryDatabase("BasicExample"));

Szolgáltatóspecifikus konfiguráció kapcsolati sztringek nélkül

Szolgáltatóspecifikus konfiguráció alkalmazásához használhat szolgáltatóspecifikus konfigurációs módszereket a kapcsolati sztring megadása nélkül. Az SQL Server-szolgáltató ehhez az esetre is tartalmazza ConfigureSqlEngine. További információkért tekintse meg az SQL Server-specifikus kötegelési viselkedést .

var services = new ServiceCollection();

services.ConfigureDbContext<BlogContext>(options =>
    options.UseSqlServer(sqlOptions => 
        sqlOptions.EnableRetryOnFailure()));

services.AddDbContext<BlogContext>(options =>
    options.UseSqlServer("connectionString"));

ConfigureDbContext és AddDbContext precedencia

Ha mindkettőt ConfigureDbContextAddDbContext használják, vagy ha több hívás történik ezekhez a metódusokhoz, a rendszer a konfigurációt a metódusok meghívásának sorrendjében alkalmazza, és a későbbi hívások elsőbbséget élveznek az ütköző lehetőségeknél.

Nem ütköző beállítások (például naplózás, elfogók vagy egyéb beállítások hozzáadása) esetén az összes konfiguráció össze van adva:

var services = new ServiceCollection();

services.ConfigureDbContext<BlogContext>(options =>
    options.LogTo(Console.WriteLine));

services.AddDbContext<BlogContext>(options =>
    options.UseInMemoryDatabase("CompositionExample"));

services.ConfigureDbContext<BlogContext>(options =>
    options.EnableSensitiveDataLogging());

Ütköző beállítások esetén az utolsó konfiguráció nyer. A viselkedésváltozással kapcsolatos további információkért tekintse meg az EF Core 8.0 jelentős változásait.

Megjegyzés:

Ha másik szolgáltatót konfigurál, az nem távolítja el az előző szolgáltatói konfigurációt. Ez hibákhoz vezethet a környezet létrehozásakor. A szolgáltató teljes cseréjéhez el kell távolítania a környezetregisztrációt, és újra hozzá kell adnia, vagy létre kell hoznia egy új szolgáltatásgyűjteményt.

További olvasás