【问题标题】:Intermittent System.ArgumentNullException using TransactionScope使用 TransactionScope 的间歇性 System.ArgumentNullException
【发布时间】:2012-09-14 15:28:20
【问题描述】:

我有一个 Windows 服务应用程序正在执行对 SQL Server 的一些调用。我有一个特定的工作单元要做,其中包括将一行保存到Message 表中并更新Buffer 表中的多行。

我已将这两个 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 块被关闭的地方,因此调用了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();
    }
}

【问题讨论】:

  • 它是多线程应用程序吗?您是否有可能在线程之间共享数据库连接?
  • 这是一个有两个线程的 Windows 服务。一个线程处理启动/停止请求并触发一个新的后台线程,该线程实际上完成了所有工作。但是第一个线程从不写入数据库,只有后台线程。
  • 它确实闻起来像一个线程安全问题,所以我不得不问:你确定只有一个后台线程吗?您是在每次数据库访问时打开/关闭连接(应该如此)还是重用连接(如果多个线程尝试使用相同的连接,这可能会导致这种情况)?创建 TransactionScope 的 Save 方法是否在后台线程(即与 DB 访问相同的线程)上运行?
  • 嗨@Joe,我的问题已经更新为可以看到线程的主要结构。

标签: c# .net transactionscope msdtc


【解决方案1】:

检查您是否已设置您的网络服务器和单独的数据库服务器(如果您将它们分开)。

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

对于日志记录,我建议在事务范围内放入一个 try catch 但是,如果您记录到数据库,则需要使用事务范围抑制功能

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

【讨论】:

  • 不涉及 Web 服务器,因为它是 Windows 服务。尽管如此,我还没有在 SQL Server 数据库所在的本地计算机上验证 DTC 设置。我会在星期一回来工作时试试这个。
  • 我检查了本地机器上的 DTC 设置,一切正常。我认为这不是问题,因为这是一个间歇性问题。如果 DTC 配置不正确,它可能根本无法工作。
  • 您是否在事务范围内记录错误?并记录您尝试保存到数据库的所有信息(消息)。查看导致此问题的信息之间是否存在特定的相关性。
【解决方案2】:

我通过阻止事务升级到 DTC 来解决这个问题。通过使用 SQL 2008 而不是 SQL 2005,transaction does not get escalated,一切都很好。

【讨论】:

    【解决方案3】:

    您没有提及您的 .Net 版本,而是根据 http://support.microsoft.com/kb/960754,2.50727.4016 版本的 System.Data.dll 存在问题。

    如果您的服务器有这个旧版本,我会尝试从 Microsoft 获取更新版本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-15
      • 2011-12-18
      • 1970-01-01
      • 2012-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多