連線恢復功能
連線復原功能會自動重試失敗的資料庫命令。 此功能可藉由提供「執行策略」來與任何資料庫搭配使用,其會封裝偵測失敗和重試命令所需的邏輯。 EF Core 提供者可以提供針對其特定資料庫失敗狀況和最佳重試原則量身打造的執行策略。
例如,SQL Server 提供者包含專為 SQL Server 量身打造的執行策略(包括 SQL Azure)。 它知道可以重試的例外狀況類型,並針對重試次數上限、重試之間的延遲等有合理的預設值。
設定內容的選項時,會指定執行策略。 這通常位於衍生內容的 方法中 OnConfiguring
:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=EFMiscellanous.ConnectionResiliency;Trusted_Connection=True;ConnectRetryCount=0",
options => options.EnableRetryOnFailure());
}
或 中的 Startup.cs
ASP.NET Core 應用程式:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PicnicContext>(
options => options.UseSqlServer(
"<connection string>",
providerOptions => providerOptions.EnableRetryOnFailure()));
}
注意
啟用失敗重試會導致 EF 在內部緩衝結果集,這可能會大幅增加傳回大型結果集之查詢的記憶體需求。 如需詳細資訊,請參閱 緩衝處理和串流 。
自訂執行策略
如果您想要變更任何預設值,有一個機制可以自行註冊自訂執行策略。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMyProvider(
"<connection string>",
options => options.ExecutionStrategy(...));
}
執行策略和交易
在失敗時自動重試的執行策略,必須能夠在失敗的重試區塊中播放每個作業。 啟用重試時,您透過 EF Core 執行的每個作業都會變成它自己的可重試作業。 也就是說,如果發生暫時性失敗,每個查詢和 對 的每個呼叫 SaveChanges()
都會以單位的形式重試。
不過,如果您的程式碼使用 BeginTransaction()
起始交易,而您正在定義需要視為單位的作業群組,而且交易內的所有專案都需要播放,就會發生失敗。 如果您在使用執行策略時嘗試這樣做,將會收到如下的例外狀況:
InvalidOperationException:已設定的執行策略 'SqlServerRetryingExecutionStrategy' 不支援使用者起始的交易。 使用 'DbContext.Database.CreateExecutionStrategy()' 所傳回的執行策略,將異動中的所有作業當做一個可重試的單位來執行。
解決方案是手動叫用執行策略,並委派代表需要執行的所有專案。 如果發生暫時性失敗,執行策略會再叫用委派一次。
using var db = new BloggingContext();
var strategy = db.Database.CreateExecutionStrategy();
strategy.Execute(
() =>
{
using var context = new BloggingContext();
using var transaction = context.Database.BeginTransaction();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
context.SaveChanges();
transaction.Commit();
});
此方法也可以與環境交易搭配使用。
using var context1 = new BloggingContext();
context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
var strategy = context1.Database.CreateExecutionStrategy();
strategy.Execute(
() =>
{
using var context2 = new BloggingContext();
using var transaction = new TransactionScope();
context2.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context2.SaveChanges();
context1.SaveChanges();
transaction.Complete();
});
交易認可失敗和等冪問題
一般而言,發生連線失敗時,會回復目前的交易。 不過,如果在認可交易時卸載連接,則交易的結果狀態為未知。
根據預設,執行策略會重試作業,就像是復原交易一樣,但如果不是這種情況,如果新的資料庫狀態不相容,或 如果作業不依賴特定狀態,可能會導致資料損毀 ,例如,插入具有自動產生索引鍵值的新資料列時。
有幾種方式可以處理這個問題。
選項 1 - 不執行任何動作 (幾乎)
在交易認可期間連線失敗的可能性很低,因此如果實際發生此狀況,您的應用程式可能會只失敗。
不過,您必須避免使用存放區產生的索引鍵,以確保擲回例外狀況,而不是新增重複的資料列。 請考慮使用用戶端產生的 GUID 值或用戶端值產生器。
選項 2 - 重建應用程式狀態
- 捨棄目前的
DbContext
。 - 建立新的
DbContext
,並從資料庫還原應用程式的狀態。 - 通知使用者,最後一項作業可能尚未順利完成。
選項 3 - 新增狀態驗證
對於變更資料庫狀態的大部分作業,可以新增檢查它是否成功的程式碼。 EF 提供擴充方法,讓這個更容易 - IExecutionStrategy.ExecuteInTransaction
。
這個方法會開始並認可交易,也接受在交易認可期間發生暫時性錯誤時所叫用參數中的 verifySucceeded
函式。
using var db = new BloggingContext();
var strategy = db.Database.CreateExecutionStrategy();
var blogToAdd = new Blog { Url = "http://blogs.msdn.com/dotnet" };
db.Blogs.Add(blogToAdd);
strategy.ExecuteInTransaction(
db,
operation: context => { context.SaveChanges(acceptAllChangesOnSuccess: false); },
verifySucceeded: context => context.Blogs.AsNoTracking().Any(b => b.BlogId == blogToAdd.BlogId));
db.ChangeTracker.AcceptAllChanges();
注意
以下是 SaveChanges
使用 設為 來 false
叫用 acceptAllChangesOnSuccess
,以避免在成功時 SaveChanges
將實體的狀態 Blog
變更為 Unchanged
。 這允許在認可失敗且交易回復時重試相同的作業。
選項 4 - 手動追蹤交易
如果您需要使用預存產生的金鑰,或需要一般的方式來處理不相依于執行每個交易的作業,可以在認可失敗時指派識別碼。
- 將資料表新增至資料庫,以追蹤交易的狀態。
- 在每個交易的開頭插入一個資料列到資料表中。
- 如果認可期間連接失敗,請檢查資料庫中對應的資料列是否存在。
- 如果認可成功,請刪除對應的資料列,以避免資料表成長。
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);
strategy.ExecuteInTransaction(
db,
operation: context => { context.SaveChanges(acceptAllChangesOnSuccess: false); },
verifySucceeded: context => context.Transactions.AsNoTracking().Any(t => t.Id == transaction.Id));
db.ChangeTracker.AcceptAllChanges();
db.Transactions.Remove(transaction);
db.SaveChanges();
注意
確定用於驗證的內容具有定義為連線在交易認可期間失敗時可能會再次失敗的執行策略。