2012-09-14 31 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获得更新的版本。

相关问题