3

SQL Server への呼び出しを実行している Windows サービス アプリケーションがあります。Message1 つの行をテーブルに保存し、テーブル内の複数の行を更新する特定の作業単位があり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();
    }
}
4

3 に答える 3

0

.Netバージョンについては言及していませんが、 http://support.microsoft.com/kb/960754によると、System.Data.dllの2.50727.4016バージョンに問題があります。

サーバーにこの古いバージョンがある場合は、Microsoftから更新されたバージョンを入手しようとします。

于 2012-11-21T07:06:55.513 に答える
0

トランザクションがDTCにエスカレートされないようにすることで、これを回避しました。SQL2005の代わりにSQL2008を使用することにより、トランザクションはエスカレートされず、すべて問題ありません。

于 2012-09-27T11:53:33.130 に答える
0

Webサーバーと別のdbサーバーを別々にセットアップしたことを確認してください。

http://itknowledgeexchange.techtarget.com/sql-server/how-to-configure-dtc-on-windows-2008/

ロギングの場合、トランザクション スコープ内に try catch を挿入することをお勧めします。ただし、データベースにロギングする場合は、トランザクション スコープの抑制機能を使用する必要があります。

 using(TransactionScope scope4 = new 
        TransactionScope(TransactionScopeOption.Suppress)) 
    {
     ...
    } 
于 2012-09-14T16:56:21.473 に答える