2012-09-14 3 views
3

SQL Server에 대한 호출을 수행하는 Windows 서비스 응용 프로그램이 있습니다. 하나의 행을 Message 테이블에 저장하고 Buffer 테이블의 여러 행을 업데이트하는 것과 관련된 특정 작업 단위가 있습니다.TransactionScope를 사용하는 간헐적 인 System.ArgumentNullException

이 두 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 블록이 닫혀있는 곳이며, 따라서 메서드를 TransactionScope이라고 부릅니다. 예외가 여기에있는 TransactionScope 클래스의 내부 동작에 의해 던져진 것처럼 보입니다.

중요한 것은 서버에 설치할 때 네트워크 액세스를 허용하는 Distributed Transaction Coordinator의 일부 설정을 활성화해야한다는 것입니다. 이것이 내 로컬 컴퓨터에있을 때 DTC 아마 사용되지 않습니다.

DTC가이 예외의 원인 일 수 있습니까?

나는 또한 연결 풀이 초과 된 것과 관련이 있는지 고려했지만 내가 얻는 것보다 더 유용한 예외가 예상됩니다. this question에서 쿼리를 계속 실행하여 연결 풀 크기를 확인했으며 절대 4를 초과하지 않았습니다.

궁극적 인 질문은이 오류가 간헐적으로 발생하는 이유입니다. 그 원인을 진단하려면 어떻게해야합니까?

편집 :

스레딩는 @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 메서드가 백그라운드 스레드 (즉, DB 액세스와 동일한 스레드)에서 실행됩니까? – Joe

답변

0

트랜잭션 범위 억제 기능의 사용을해야합니다 DTC로 에스컬레이트되지 않도록 트랜잭션을 중지합니다.SQL 2005 대신 SQL 2008을 사용하면 transaction does not get escalated과 모두 괜찮습니다.

0

별도로 설정 한 경우 웹 서버와 별도의 db 서버를 설정했는지 확인하십시오. 당신이 데이터베이스에 로그인하면 내가 트랜잭션 범위 내에서 시도 캐치하는 int 넣어 제안 로깅을위한

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

그러나 당신은 내가 해결할

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

Windows 서비스이므로 웹 서버가 필요 없습니다. 하지만 SQL Server 데이터베이스가있는 로컬 컴퓨터에서 DTC 설정을 확인하지 않았습니다. 나는 월요일에 다시 일할 때 이것을 시도 할 것이다. –

+0

로컬 컴퓨터에서 DTC 설치를 확인했는데 문제가 없습니다. 이것은 간헐적 인 문제이므로 이것이 문제는 아니라고 생각합니다. DTC가 올바르게 구성되지 않은 경우에는 DTC가 전혀 작동하지 않습니다. –

+0

트랜잭션 범위 내에 오류를 기록 했습니까? 데이터베이스 (Message)에 저장하려고하는 모든 정보를 기록하십시오. 어떤 정보가이 문제를 일으키는 지 사이에 특별한 상관 관계가 있는지 확인하십시오. – P6345uk

0

.Net 버전은 언급하지 않지만 http://support.microsoft.com/kb/960754에 따르면 System.Data.dll 2.50727.4016 버전에 문제가 있습니다.

서버에이 이전 버전이있는 경우 Microsoft에서 업데이트 된 버전을 얻으려고합니다.

관련 문제