Kapcsolat rugalmassága
A kapcsolat rugalmassága automatikusan újrapróbálkozza a sikertelen adatbázis-parancsokat. A funkció bármely adatbázissal használható egy "végrehajtási stratégia" biztosításával, amely magában foglalja a hibák észleléséhez és a parancsok újrapróbálkozásához szükséges logikát. Az EF Core-szolgáltatók a konkrét adatbázis-meghibásodási feltételekhez és az optimális újrapróbálkozási szabályzathoz szabott végrehajtási stratégiákat biztosítanak.
Az SQL Server-szolgáltató például egy kifejezetten az SQL Serverhez (beleértve az SQL Azure-t is) szabott végrehajtási stratégiát tartalmaz. Tisztában van az újrapróbálkozható kivételtípusokkal, és ésszerű alapértelmezett értékekkel rendelkezik a maximális újrapróbálkozásokhoz, az újrapróbálkozások közötti késleltetéshez stb.
A környezet beállításainak konfigurálásakor végrehajtási stratégia van megadva. Ez általában a származtatott környezet OnConfiguring
metódusában történik:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=EFMiscellanous.ConnectionResiliency;Trusted_Connection=True;ConnectRetryCount=0",
options => options.EnableRetryOnFailure());
}
vagy Startup.cs
egy ASP.NET Core-alkalmazás esetében:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PicnicContext>(
options => options.UseSqlServer(
"<connection string>",
providerOptions => providerOptions.EnableRetryOnFailure()));
}
Jegyzet
A sikertelen újrapróbálkozások engedélyezése miatt az EF belsőleg puffereli az eredményhalmazt, ami jelentősen megnövelheti a nagy eredményhalmazokat visszaküldött lekérdezések memóriaigényét. További részletekért lásd pufferezés és streamelés.
Egyéni végrehajtási stratégia
Ha módosítani szeretné az alapértelmezett beállításokat, egy saját egyéni végrehajtási stratégiát regisztrálhat.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMyProvider(
"<connection string>",
options => options.ExecutionStrategy(...));
}
Végrehajtási stratégiák és tranzakciók
Az a végrehajtási stratégia, amely automatikusan újrapróbálkozik a hibák esetén, képesnek kell lennie minden sikertelen művelet újbóli végrehajtására az újrapróbálkozási blokkban. Ha az újrapróbálkozás engedélyezve van, az EF Core-on keresztül végrehajtott minden művelet saját újrapróbálkozási művelet lesz. Ez azt jelzi, hogy a rendszer minden lekérdezést és SaveChangesAsync()
hívását egységként újrapróbálkozza, ha átmeneti hiba történik.
Ha azonban a kód egy tranzakciót kezdeményez a BeginTransactionAsync()
használatával, Ön meghatározza a saját műveletcsoportját, amelyet egységként kell kezelni, és a tranzakción belül mindennek vissza kell játszódnia, ha hiba történik. Ha végrehajtási stratégia használata közben megpróbálja ezt tenni, a következőhöz hasonló kivételt fog kapni:
InvalidOperationException: Az "SqlServerRetryingExecutionStrategy" konfigurált végrehajtási stratégia nem támogatja a felhasználó által kezdeményezett tranzakciókat. Használja a DbContext.Database.CreateExecutionStrategy()) által visszaadott végrehajtási stratégiát a tranzakció összes műveletének újrapróbálkozható egységként való végrehajtásához.
A megoldás az, hogy manuálisan meghívja a végrehajtási stratégiát egy delegátummal, amely mindent képvisel, amit végre kell hajtani. Átmeneti hiba esetén a végrehajtási stratégia újra meghívja a meghatalmazottat.
using var db = new BloggingContext();
var strategy = db.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(
async () =>
{
using var context = new BloggingContext();
await using var transaction = await context.Database.BeginTransactionAsync();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
await context.SaveChangesAsync();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
await context.SaveChangesAsync();
await transaction.CommitAsync();
});
Ez a megközelítés környezeti tranzakciókkal is használható.
using var context1 = new BloggingContext();
context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
var strategy = context1.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(
async () =>
{
using var context2 = new BloggingContext();
using var transaction = new TransactionScope();
context2.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
await context2.SaveChangesAsync();
await context1.SaveChangesAsync();
transaction.Complete();
});
Tranzakció véglegesítési hibája és az idempotencia problémája
Általában csatlakozási hiba esetén a rendszer visszaállítja az aktuális tranzakciót. Ha azonban a kapcsolat megszakad a tranzakció véglegesítése közben, a tranzakció eredményül kapott állapota ismeretlen.
Alapértelmezés szerint a végrehajtási stratégia újrapróbálkozza a műveletet, mintha a tranzakció vissza lett volna állítva, de ha nem ez a helyzet, ez kivételt eredményez, ha az új adatbázis állapota nem kompatibilis, vagy adatsérüléshez vezethet,, ha a művelet nem egy adott állapotra támaszkodik, például ha egy automatikusan létrehozott kulcsértékekkel rendelkező új sort szúr be.
Ezt többféleképpen is megoldhatja.
1. lehetőség – Semmit sem tehet (majdnem)
A tranzakció véglegesítése során a kapcsolat meghibásodásának valószínűsége alacsony, ezért elfogadható lehet, hogy az alkalmazás egyszerűen meghiúsuljon, ha ez a feltétel ténylegesen bekövetkezik.
Azonban el kell kerülnie az adatbázis által generált kulcsok használatát annak érdekében, hogy kivétel keletkezzen az ismétlődő sor hozzáadása helyett. Fontolja meg az ügyfél által létrehozott GUID-érték vagy ügyféloldali értékgenerátor használatát.
2. lehetőség – Az alkalmazás állapotának újraépítése
- Az aktuális
DbContext
elvetése. - Hozzon létre új
DbContext
-t, és állítsa vissza az alkalmazás állapotát az adatbázisból. - Tájékoztassa a felhasználót, hogy előfordulhat, hogy az utolsó művelet nem fejeződött be sikeresen.
3. lehetőség – Állapot-ellenőrzés hozzáadása
Az adatbázis állapotát módosító műveletek többségéhez olyan kódot lehet hozzáadni, amely ellenőrzi, hogy sikerült-e. Az EF egy bővítmény-módszert biztosít, hogy ezt megkönnyítse - IExecutionStrategy.ExecuteInTransaction
.
Ez a metódus elindítja és véglegesíti a tranzakciót, és elfogadja a verifySucceeded
paraméter azon függvényét is, amely akkor lesz meghívva, ha átmeneti hiba történik a tranzakció véglegesítése során.
using var db = new BloggingContext();
var strategy = db.Database.CreateExecutionStrategy();
var blogToAdd = new Blog { Url = "http://blogs.msdn.com/dotnet" };
db.Blogs.Add(blogToAdd);
await strategy.ExecuteInTransactionAsync(
db,
operation: (context, cancellationToken) => context.SaveChangesAsync(acceptAllChangesOnSuccess: false, cancellationToken),
verifySucceeded: (context, cancellationToken) => context.Blogs.AsNoTracking().AnyAsync(b => b.BlogId == blogToAdd.BlogId, cancellationToken));
db.ChangeTracker.AcceptAllChanges();
Jegyzet
Itt a(z) SaveChanges
hívódik meg úgy, hogy a(z) acceptAllChangesOnSuccess
false
-re van állítva annak érdekében, hogy elkerüljük a(z) Blog
entitás állapotának Unchanged
-re változtatását, ha a(z) SaveChanges
sikerül. Ez lehetővé teszi ugyanazt a műveletet újrapróbálkozást, ha a véglegesítés meghiúsul, és a tranzakció vissza lesz állítva.
4. lehetőség – A tranzakció manuális nyomon követése
Ha tároló által létrehozott kulcsokat kell használnia, vagy olyan általános módszerre van szüksége a véglegesítési hibák kezelésére, amely nem függ az egyes tranzakciók által végrehajtott művelettől, hozzárendelhető egy azonosító, amelyet a véglegesítés sikertelenségekor ellenőriz.
- Adjon hozzá egy táblát az adatbázishoz, amely a tranzakciók állapotának nyomon követésére szolgál.
- Szúrjon be egy sort a táblába az egyes tranzakciók elején.
- Ha a kapcsolat a véglegesítés során meghiúsul, ellenőrizze, hogy a megfelelő sor szerepel-e az adatbázisban.
- Ha a véglegesítés sikeres, törölje a megfelelő sort a tábla növekedésének elkerülése érdekében.
using var db = new BloggingContext();
var strategy = db.Database.CreateExecutionStrategy();
db.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
var transaction = new TransactionRow { Id = Guid.NewGuid() };
db.Transactions.Add(transaction);
await strategy.ExecuteInTransactionAsync(
db,
operation: (context, cancellationToken) => context.SaveChangesAsync(acceptAllChangesOnSuccess: false, cancellationToken),
verifySucceeded: (context, cancellationToken) => context.Transactions.AsNoTracking().AnyAsync(t => t.Id == transaction.Id, cancellationToken));
db.ChangeTracker.AcceptAllChanges();
db.Transactions.Remove(transaction);
await db.SaveChangesAsync();
Jegyzet
Győződjön meg arról, hogy az ellenőrzéshez használt környezet rendelkezik egy végrehajtási stratégiával, amely úgy van meghatározva, hogy a kapcsolat valószínűleg ismét sikertelen lesz az ellenőrzés során, ha a tranzakció véglegesítése során meghiúsult.