使用的TransactionScope间歇System.ArgumentNullException [英] Intermittent System.ArgumentNullException using TransactionScope

查看:175
本文介绍了使用的TransactionScope间歇System.ArgumentNullException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个正在执行一些调用SQL Server的Windows服务应用程序。我有工作的特定单位做这涉及到保存一行到消息表和缓存表。

我裹着这两个SQL语句组合成一个的TransactionScope ,以确保它们要么都得到承诺,或没有得到承诺。

高水平code是这样的:

 公共静态无效保存(消息消息)
{
    使用(VAR的TransactionScope =新的TransactionScope())
    {
        MessageData.Save(message.TransactionType,
                         message.Version,
                         message.CaseNumber,
                         message.Route code,
                         message.BufferSetIdentifier,
                         message.InternalPatientNumber,
                         message.DistrictNumber,
                         message.Data,
                         message.DateAssembled,
                         (字节)MessageState.Inserted);

        BufferLogic.FlagSetAsAssembled(message.BufferSetIdentifier);

        transactionScope.Complete();
    }
}
 

这已全部使用本地SQL Server安装工作完全在我的开发机器。

在部署Windows服务的服务器(但连接回我的本地机器上的SQL Server)的我的间歇收到此错误信息:

  System.ArgumentNullException:值不能为空。
   在System.Threading.Monitor.Enter(obj对象)
   在System.Data.ProviderBase.DbConnectionPool.TransactedConnectionPool.TransactionEnded(交易成交,DbConnectionInternal transactedObject)
   在System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment入伍)
   在System.Transactions.TransactionStateDelegatedCommitting.EnterState(InternalTransaction TX)
   在System.Transactions.CommittableTransaction.Commit()
   在System.Transactions.TransactionScope.InternalDispose()
   在System.Transactions.TransactionScope.Dispose()
   在OpenLink.Logic.MessageLogic.Save(消息消息)在E:\ DevTFS \ P0628Temp \ OpenLink公司\ OpenLink.Logic \ MessageLogic.cs:行30
   在OpenLinkMessageAssembler.OpenLinkMessageAssemblerService.RunService()在E:\ DevTFS \ P0628Temp \ OpenLink公司\ OpenLinkMessageAssembler \ OpenLinkMessageAssemblerService.cs:行99
 

我相信code线由异常所指的正是其中使用块被关闭,因此调用的Dispose( )的TransactionScope 的方法。我在一个小的损失的就在这里,作为例外似乎是由内部运作抛出的的TransactionScope 类。

这可能是显著一件事是,在服务器上安装的时候,我不得不启用一些为分布式事务处理协调器的设置,以允许网络访问这引起了我以为,当这一切都在我的本地机器上,DTC是可能不被使用。

难道DTC是这个异常的原因的一部分吗?

我也考虑过是否有人做被刷爆了连接池,但希望比我得到一个更有用的异常。我不停地奔跑在<一个查询href="http://stackoverflow.com/questions/2222924/transactionscope-helper-that-exhausts-connection-pool-without-fail-help">this问题检查连接池的大小,而且也从未突破400。

我的最后一个问题是,为什么这个错误发生间歇性?如何诊断是什么引起的呢?

编辑:线程

@Joe暗示这可能是一个线程问题。因此,我在下面提供了我的Windows服务的骨架code,看它是否是有问题的。

注意事件记录类只写入Windows事件日志,并没有连接到SQL Server。

 部分类OpenLinkMessageAssemblerService:ServiceBase的
{
    私人挥发性布尔_isStopping;
    私人只读ManualResetEvent的_stoppedEvent;
    私人只读INT _stopTimeout = Convert.ToInt32(ConfigurationManager.AppSettings [ServiceOnStopTimeout]);
    螺纹_workerThread;

    公共OpenLinkMessageAssemblerService()
    {
        的InitializeComponent();
        _isStopping = FALSE;
        _stoppedEvent =新的ManualResetEvent(假);
        服务名称=OpenLinkMessageAssembler;
    }

    保护覆盖无效的OnStart(字串[] args)
    {
        尝试
        {
            _workerThread =新主题(RunService){的IsBackground = TRUE};
            _workerThread.Start();
        }
        赶上(例外的例外)
        {
            EventLogger.LogError(服务名,exception.ToString());
            扔;
        }
    }

    保护覆盖无效的onStop()
    {
        //设置全局标志,因此它可以被拾起的工作线程
        _isStopping = TRUE;

        //允许工作线程,直到发生超时完全退出
        如果(!_stoppedEvent.WaitOne(_stopTimeout))
        {
            _workerThread.Abort();
        }
    }

    私人无效RunService()
    {
        //检查全局标志,指示是否服务已被告知停止
        而(!_isStopping)
        {
            尝试
            {
                变种buffersToAssemble = BufferLogic.GetNextSetForAssembly();

                如果(!buffersToAssemble.Any())
                {
                    Thread.sleep代码(30000);
                    继续;
                }

                ... //一些验证code删除这里的清晰度

                字符串assembledMessage =的String.Empty;
                buffersToAssemble.ForEach(B =&GT; assembledMessage + = b.Data);

                VAR messageParser =新MessageParser(assembledMessage);
                VAR消息= messageParser.Parse();

                MessageLogic.Save(消息); //&所述;  - 这要求这导致异常的方法
            }
            赶上(例外的例外)
            {
                EventLogger.LogError(服务名,exception.ToString());
                扔;
            }
        }
        _stoppedEvent.Set();
    }
}
 

解决方案

我工作围绕这被升级到DTC停止交易。通过使用SQL Server 2008而不是SQL 2005年,<一个href="http://stackoverflow.com/questions/1690892/transactionscope-automatically-escalating-to-msdtc-on-some-machines">transaction没有得到升级,所有的罚款。

I have a Windows Service application which is performing some calls to SQL Server. I have a particular unit of work to do which involves saving one row to the Message table and updating multiple rows in the Buffer table.

I have wrapped these two SQL statements into a TransactionScope to ensure that they either both get committed, or neither get committed.

The high level code looks like this:

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

This has all worked perfectly on my development machine with a local SQL Server installation.

On deploying the Windows Service to a server (but connecting back to my local machine's SQL Server) I am intermittently getting this error message:

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

I believe the line of code being referred to by the exception is where the using block is closed, thus calling the Dispose() method of the TransactionScope. I'm at a bit of a loss here, as the exception seems to be thrown by the internal workings of the TransactionScope class.

One thing that may be significant is that when installing on the server, I had to enable some of the settings for the Distributed Transaction Coordinator to allow network access This got me into thinking that when it's all on my local machine, DTC is probably not used.

Could DTC be part of the cause of this exception?

I also considered whether it was to do with connection pools being maxed out, but would expect a more useful exception than what I'm getting. I kept running the query in this question to check the connection pool size, and it never exceeded four.

My ultimate question is, why is this error intermittently occurring? How can I diagnose what's causing it?

Edit: Threading

@Joe suggested this could be a threading issue. I've therefore included the skeleton code of my Windows Service below to see if it is problematic.

Note that the EventLogger class writes only to the Windows event log and does not connect to 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();
    }
}

解决方案

I worked around this by stopping the transaction from being escalated to DTC. By using SQL 2008 instead of SQL 2005, the transaction does not get escalated, and all is fine.

这篇关于使用的TransactionScope间歇System.ArgumentNullException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆