SQL Server への呼び出しを実行している Windows サービス アプリケーションがあります。Message
1 つの行をテーブルに保存し、テーブル内の複数の行を更新する特定の作業単位がありBuffer
ます。
これら 2 つの SQL ステートメントを にラップTransactionScope
して、両方がコミットされるか、どちらもコミットされないようにします。
高レベルのコードは次のようになります。
public static void Save(Message message)
{
using (var transactionScope = new TransactionScope())
{
MessageData.Save(message.TransactionType,
message.Version,
message.CaseNumber,
message.RouteCode,
message.BufferSetIdentifier,
message.InternalPatientNumber,
message.DistrictNumber,
message.Data,
message.DateAssembled,
(byte)MessageState.Inserted);
BufferLogic.FlagSetAsAssembled(message.BufferSetIdentifier);
transactionScope.Complete();
}
}
これはすべて、ローカルに SQL Server をインストールした私の開発マシンで完全に機能しました。
Windows サービスをサーバーにデプロイする (ただし、ローカル マシンの SQL Server に接続し直す) と、断続的に次のエラー メッセージが表示されます。
System.ArgumentNullException: Value cannot be null.
at System.Threading.Monitor.Enter(Object obj)
at System.Data.ProviderBase.DbConnectionPool.TransactedConnectionPool.TransactionEnded(Transaction transaction, DbConnectionInternal transactedObject)
at System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment enlistment)
at System.Transactions.TransactionStateDelegatedCommitting.EnterState(InternalTransaction tx)
at System.Transactions.CommittableTransaction.Commit()
at System.Transactions.TransactionScope.InternalDispose()
at System.Transactions.TransactionScope.Dispose()
at OpenLink.Logic.MessageLogic.Save(Message message) in E:\DevTFS\P0628Temp\OpenLink\OpenLink.Logic\MessageLogic.cs:line 30
at OpenLinkMessageAssembler.OpenLinkMessageAssemblerService.RunService() in E:\DevTFS\P0628Temp\OpenLink\OpenLinkMessageAssembler\OpenLinkMessageAssemblerService.cs:line 99
例外によって参照されているコード行は、using
ブロックが閉じられている場所でDispose()
あり、TransactionScope
. TransactionScope
クラスの内部動作によって例外がスローされるように見えるため、ここでは少し途方に暮れています。
重要なことの 1 つは、サーバーにインストールするときに、ネットワーク アクセスを許可するために分散トランザクション コーディネーターの設定の一部を有効にする必要があったことです。これにより、すべてがローカル マシン上にある場合、DTC はおそらく使用されないのではないかと考えるようになりました。 .
DTC がこの例外の原因の一部である可能性はありますか?
また、接続プールが上限に達したことに関係があるかどうかも検討しましたが、私が得ているものよりも便利な例外が予想されます。接続プールのサイズを確認するためにこの質問のクエリを実行し続けましたが、4 を超えることはありませんでした。
私の最終的な質問は、なぜこのエラーが断続的に発生するのですか? 何が原因かを診断するにはどうすればよいですか?
編集:スレッド
@Joe は、これがスレッド化の問題である可能性があると示唆しました。したがって、問題があるかどうかを確認するために、Windows サービスのスケルトン コードを以下に含めました。
EventLogger
クラスは Windows イベント ログにのみ書き込み、SQL Server には接続しないことに注意してください。
partial class OpenLinkMessageAssemblerService : ServiceBase
{
private volatile bool _isStopping;
private readonly ManualResetEvent _stoppedEvent;
private readonly int _stopTimeout = Convert.ToInt32(ConfigurationManager.AppSettings["ServiceOnStopTimeout"]);
Thread _workerThread;
public OpenLinkMessageAssemblerService()
{
InitializeComponent();
_isStopping = false;
_stoppedEvent = new ManualResetEvent(false);
ServiceName = "OpenLinkMessageAssembler";
}
protected override void OnStart(string[] args)
{
try
{
_workerThread = new Thread(RunService) { IsBackground = true };
_workerThread.Start();
}
catch (Exception exception)
{
EventLogger.LogError(ServiceName, exception.ToString());
throw;
}
}
protected override void OnStop()
{
// Set the global flag so it can be picked up by the worker thread
_isStopping = true;
// Allow worker thread to exit cleanly until timeout occurs
if (!_stoppedEvent.WaitOne(_stopTimeout))
{
_workerThread.Abort();
}
}
private void RunService()
{
// Check global flag which indicates whether service has been told to stop
while (!_isStopping)
{
try
{
var buffersToAssemble = BufferLogic.GetNextSetForAssembly();
if (!buffersToAssemble.Any())
{
Thread.Sleep(30000);
continue;
}
... // Some validation code removed here for clarity
string assembledMessage = string.Empty;
buffersToAssemble.ForEach(b => assembledMessage += b.Data);
var messageParser = new MessageParser(assembledMessage);
var message = messageParser.Parse();
MessageLogic.Save(message); // <-- This calls the method which results in the exception
}
catch (Exception exception)
{
EventLogger.LogError(ServiceName, exception.ToString());
throw;
}
}
_stoppedEvent.Set();
}
}