2012-09-14 53 views
3

我有一個正在執行一些調用SQL Server的Windows服務應用程序。我有一個特定的工作單元要做,其中包括將一行保存到Message表並更新Buffer表中的多行。Intermittent System.ArgumentNullException使用TransactionScope

我已經將這兩條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服務器)我間歇收到此錯誤信息:

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塊關閉,因此調用TransactionScopeDispose()方法。我在這裏遇到了一些問題,因爲TransactionScope課程的內部工作似乎引發了異常情況。

可能很重要的一件事是,在服務器上安裝時,我必須啓用分佈式事務處理協調器的一些設置才能允許網絡訪問這讓我想到,當它全部在我的本地計算機上時,DTC可能沒有使用。

DTC可能是此異常原因的一部分嗎?

我也考慮過是否要處理最大連接池,但會比我得到的更有用的異常。我繼續運行this question中的查詢來檢查連接池大小,並且它從未超過四個。

我最終的問題是,爲什麼這個錯誤會間歇性地發生?我如何診斷導致它的原因?

編輯:線程

@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(); 
    } 
} 
+0

它是一個多線程應用程序嗎?是否有可能在線程之間共享數據庫連接? – Joe

+0

這是一個帶有兩個線程的Windows服務。一個線程處理啓動/停止請求,並觸發一個實際完成所有工作的新後臺線程。然而,第一個線程永遠不會寫入數據庫,只有後臺線程。 –

+0

它聞起來像一個線程安全問題,所以我不得不問:你絕對肯定只能有一個後臺線程?你打開/關閉每個數據庫訪問的連接(如你應該的那樣)還是重用連接(如果多個線程嘗試使用相同的連接,這可能會導致這種情況)?創建TransactionScope的Save方法是否在後臺線程(即與數據庫訪問相同的線程)上運行? – Joe

回答

0

檢查已經設置你的Web服務器和獨立的數據庫服務器,如果你有他們分開。

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

日誌記錄,我建議把INT一個嘗試捕捉交易範圍內。然而,如果你登錄到數據庫,你需要使用事務範圍抑制功能的

using(TransactionScope scope4 = new 
     TransactionScope(TransactionScopeOption.Suppress)) 
    { 
    ... 
    } 
+0

因爲它是Windows服務,所以沒有涉及Web服務器。儘管如此,我還沒有驗證SQL Server數據庫所在的本地機器上的DTC設置。我會在週一回到工作時試試這個。 –

+0

我檢查了我的本地機器上的DTC設置,它很好。我不認爲這是問題,因爲這是一個間歇性問題。如果DTC配置不正確,它可能根本不起作用。 –

+0

您是否在事務範圍內記錄錯誤?並將您試圖保存的所有信息記錄到數據庫(消息)。以查看是什麼信息導致此問題之間存在特定的相關性。 – P6345uk

0

你沒有提到你的.Net版本,但是根據 http://support.microsoft.com/kb/960754,System.Data.dll的版本是2.50727.4016。

如果您的服務器有這個舊版本,我會嘗試從Microsoft獲得更新的版本。

相關問題