SQL Server 2012 に対して実行する C# OLTP アプリケーションがあります。データベースが AlwaysOn 可用性グループにある場合、フェールオーバーが発生した場合でも、アプリケーションを中断することなく実行したいと考えています。フェイルオーバーによって中断されたトランザクションから回復するために、C# SqlCommand オブジェクトを別のクラス (RetryableSqlCommand) でラップしました。このクラスは、フェイルオーバーに関連する例外 (接続が切断された、リモート トランザクションを強化できなかったなど) をキャッチし、それらを自動的に再試行します。指数関数的なバックオフ遅延の後。小さなテスト アプリでは、このコードは思いどおりに機能します。完全な OLTP アプリで中程度の負荷 (1 秒あたり数百トランザクション) で実行すると、データベースを手動でフェールオーバーするとエラーが発生します。
エラーは、1 つのマスタ レコードと 4 つの詳細レコードを挿入する、最も頻繁に呼び出されるストアド プロシージャの 1 つで常に発生します。ストアド プロシージャは常に、挿入されたマスター レコードと詳細レコードの ID を OUTPUT パラメーターとして返します。C# コードは次のようになります。
using (RetryableSqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "InsertComplexItem";
cmd.CommandType = CommandType.StoredProcedure;
// add sproc input parameters for master and detail records
await cmd.ExecuteNonQueryAsync();
// retrieve IDs of inserted records
var masterId = (long)(cmd.Parameters["MasterId"].Value); <-- InvalidCastException
var detail1Id = (long)(cmd.Parameters["Detail1Id"].Value);
var detail2Id = (long)(cmd.Parameters["Detail2Id"].Value);
var detail3Id = (long)(cmd.Parameters["Detail3Id"].Value);
var detail4Id = (long)(cmd.Parameters["Detail4Id"].Value);
}
上記の行で InvalidCastException が発生します。これは、デバッグ ログから判断したように、ID がストアド プロシージャから DBNull として返されているためです。私が知る限り、ストアド プロシージャはすべてのレコードを 1 つのトランザクションに挿入するため、このようなことは決して起こらないはずです。トランザクションが中断された場合、RetryableSqlCommand クラスによってキャッチされ、再試行される例外が存在するはずです。InvalidCastException の説明はありますか?