3

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

我将这两个 SQL 语句包装成 aTransactionScope以确保它们要么都被提交,要么都不被提交。

高级代码如下所示:

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该类的内部工作似乎引发了异常。

可能很重要的一件事是,在服务器上安装时,我必须启用分布式事务协调器的一些设置以允许网络访问这让我想到,当它全部在我的本地计算机上时,可能不使用 DTC .

DTC 可能是导致此异常的部分原因吗?

我还考虑了这是否与连接池被最大化有关,但预计会有一个比我得到的更有用的例外。我一直在这个问题中运行查询来检查连接池大小,它从未超过四个。

我的最终问题是,为什么这个错误会间歇性地发生?如何诊断是什么原因造成的?

编辑:线程

@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

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

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 回答
0

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

于 2012-09-27T11:53:33.130 回答
0

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

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

于 2012-11-21T07:06:55.513 回答