Menerapkan sambungan SQL Core Kerangka Kerja Entitas yang tangguh
Tip
Konten ini adalah kutipan dari eBook, .NET Microservices Architecture for Containerized .NET Applications, tersedia di .NET Docs atau sebagai PDF yang dapat diunduh gratis dan dapat dibaca secara offline.
Untuk Azure SQL Database, Entity Kerangka Kerja (EF) Core sudah menyediakan ketahanan sambungan database internal dan logika coba lagi. Tetapi Anda perlu mengaktifkan strategi eksekusi Kerangka Kerja Entitas untuk setiap DbContext sambungan jika Anda ingin memiliki sambungan Inti EF yang tangguh.
Misalnya, kode berikut di tingkat sambungan Inti EF memungkinkan sambungan SQL tangguh yang dicoba kembali jika sambungan gagal.
// Program.cs from any ASP.NET Core Web API
// Other code ...
builder.Services.AddDbContext<CatalogContext>(options =>
{
options.UseSqlServer(builder.Configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
});
});
Strategi eksekusi dan transaksi eksplisit menggunakan BeginTransaction dan beberapa DbContexts
Ketika percobaan ulang diaktifkan dalam sambungan Inti EF, setiap operasi yang Anda lakukan menggunakan Inti EF menjadi operasi yang dapat diulang sendiri. Setiap kueri dan setiap panggilan ke SaveChanges
akan dicoba kembali sebagai unit jika terjadi kegagalan sementara.
Tetapi, jika kode Anda memulai transaksi menggunakan BeginTransaction
, Anda menentukan grup operasi Anda sendiri yang perlu ditangani sebagai unit. Segala sesuatu di dalam transaksi harus digulung balik jika terjadi kegagalan.
Jika Anda mencoba menjalankan transaksi tersebut saat menggunakan strategi eksekusi EF (kebijakan coba lagi) dan Anda memanggil SaveChanges
dari beberapa DbContexts, Anda akan mendapatkan pengecualian seperti ini:
System.InvalidOperationException: Strategi eksekusi yang dikonfigurasi 'SqlServerRetryingExecutionStrategy' tidak mendukung transaksi yang dimulai pengguna. Gunakan strategi eksekusi yang dikembalikan oleh 'DbContext.Database.CreateExecutionStrategy()' untuk menjalankan semua operasi dalam transaksi sebagai unit yang dapat dicoba kembali.
Solusinya adalah memanggil strategi eksekusi EF secara manual dengan delegasi yang mewakili semua yang perlu dijalankan. Jika terjadi kegagalan sementara, strategi eksekusi akan memanggil delegasi lagi. Misalnya, kode berikut menunjukkan bagaimana penerapannya di eShopOnContainers dengan dua beberapa DbContexts (_catalogContext dan IntegrationEventLogContext) saat memperbarui produk lalu menyimpan objek ProductPriceChangedIntegrationEvent, yang perlu menggunakan DbContext berbeda.
public async Task<IActionResult> UpdateProduct(
[FromBody]CatalogItem productToUpdate)
{
// Other code ...
var oldPrice = catalogItem.Price;
var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price;
// Update current product
catalogItem = productToUpdate;
// Save product's data and publish integration event through the Event Bus
// if price has changed
if (raiseProductPriceChangedEvent)
{
//Create Integration Event to be published through the Event Bus
var priceChangedEvent = new ProductPriceChangedIntegrationEvent(
catalogItem.Id, productToUpdate.Price, oldPrice);
// Achieving atomicity between original Catalog database operation and the
// IntegrationEventLog thanks to a local transaction
await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(
priceChangedEvent);
// Publish through the Event Bus and mark the saved event as published
await _catalogIntegrationEventService.PublishThroughEventBusAsync(
priceChangedEvent);
}
// Just save the updated product because the Product's Price hasn't changed.
else
{
await _catalogContext.SaveChangesAsync();
}
}
DbContext pertama adalah _catalogContext
dan DbContext
kedua berada dalam objek _catalogIntegrationEventService
. Tindakan Penerapan dilakukan di semua objek DbContext
menggunakan strategi eksekusi EF.
Untuk mencapai beberapa penerapan DbContext
ini, SaveEventAndCatalogContextChangesAsync
menggunakan kelas ResilientTransaction
, seperti yang ditunjukkan dalam kode berikut:
public class CatalogIntegrationEventService : ICatalogIntegrationEventService
{
//…
public async Task SaveEventAndCatalogContextChangesAsync(
IntegrationEvent evt)
{
// Use of an EF Core resiliency strategy when using multiple DbContexts
// within an explicit BeginTransaction():
// https://learn.microsoft.com/ef/core/miscellaneous/connection-resiliency
await ResilientTransaction.New(_catalogContext).ExecuteAsync(async () =>
{
// Achieving atomicity between original catalog database
// operation and the IntegrationEventLog thanks to a local transaction
await _catalogContext.SaveChangesAsync();
await _eventLogService.SaveEventAsync(evt,
_catalogContext.Database.CurrentTransaction.GetDbTransaction());
});
}
}
Metode ResilientTransaction.ExecuteAsync
ini pada dasarnya memulai transaksi dari yang diteruskan DbContext
(_catalogContext
) lalu menjadikan EventLogService
menggunakan transaksi tersebut untuk menyimpan perubahan dari IntegrationEventLogContext
dan kemudian melakukan seluruh transaksi.
public class ResilientTransaction
{
private DbContext _context;
private ResilientTransaction(DbContext context) =>
_context = context ?? throw new ArgumentNullException(nameof(context));
public static ResilientTransaction New (DbContext context) =>
new ResilientTransaction(context);
public async Task ExecuteAsync(Func<Task> action)
{
// Use of an EF Core resiliency strategy when using multiple DbContexts
// within an explicit BeginTransaction():
// https://learn.microsoft.com/ef/core/miscellaneous/connection-resiliency
var strategy = _context.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
await using var transaction = await _context.Database.BeginTransactionAsync();
await action();
await transaction.CommitAsync();
});
}
}
Sumber daya tambahan
Ketahanan Sambungan dan Intersepsi Perintah dengan EF dalam Aplikasi MVC ASP.NET
https://learn.microsoft.com/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/connection-resiliency-and-command-interception-with-the-entity-framework-in-an-asp-net-mvc-applicationCesar de la Torre. Menggunakan Sambungan dan Transaksi SQL Inti Kerangka Kerja Entitas Tangguh
https://devblogs.microsoft.com/cesardelatorre/using-resilient-entity-framework-core-sql-connections-and-transactions-retries-with-exponential-backoff/
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk