2009-11-09 7 views
0

응용 프로그램의 일부로 SQL Server Express 서비스에 프로그래밍 방식으로 액세스해야합니다. 사용자가하려는 작업에 따라 데이터베이스를 연결하거나 데이터베이스를 분리하거나 백업해야 할 수도 있습니다. 이러한 작업을 시도하기 전에 서비스가 시작되지 않을 수도 있습니다. 따라서 우리는 서비스가 시작되었는지 확인해야합니다. 여기 우리가 문제를 겪고있는 곳이 있습니다. 분명히 ServiceController.WaitForStatus(ServiceControllerStatus.Running)은 SQL Server Express에 대해 조기에 반환됩니다. 정말 수수께끼 같은 점은 마스터 데이터베이스는 즉시 사용할 수 있지만 다른 데이터베이스는 사용할 수없는 것입니다. 여기에 내가 말하고있는 것을 보여주기위한 콘솔 응용 프로그램이 있습니다 :시작된 서비스가 시작한 서비스는 언제입니까? (SQL Express)

namespace ServiceTest 
{ 
    using System; 
    using System.Data.SqlClient; 
    using System.Diagnostics; 
    using System.ServiceProcess; 
    using System.Threading; 

    class Program 
    { 
     private static readonly ServiceController controller = new ServiceController("MSSQL$SQLEXPRESS"); 
     private static readonly Stopwatch stopWatch = new Stopwatch(); 

     static void Main(string[] args) 
     { 
      stopWatch.Start(); 

      EnsureStop(); 
      Start(); 
      OpenAndClose("master"); 

      EnsureStop(); 
      Start(); 
      OpenAndClose("AdventureWorksLT"); 

      Console.ReadLine(); 
     } 

     private static void EnsureStop() 
     { 
      Console.WriteLine("EnsureStop enter, {0:N0}", stopWatch.ElapsedMilliseconds); 

      if (controller.Status != ServiceControllerStatus.Stopped) 
      { 
       controller.Stop(); 
       controller.WaitForStatus(ServiceControllerStatus.Stopped); 
       Thread.Sleep(5000); // really, really make sure it stopped ... this has a problem too. 
      } 

      Console.WriteLine("EnsureStop exit, {0:N0}", stopWatch.ElapsedMilliseconds); 
     } 

     private static void Start() 
     { 
      Console.WriteLine("Start enter, {0:N0}", stopWatch.ElapsedMilliseconds); 
      controller.Start(); 
      controller.WaitForStatus(ServiceControllerStatus.Running); 
      // Thread.Sleep(5000); 
      Console.WriteLine("Start exit, {0:N0}", stopWatch.ElapsedMilliseconds); 
     } 

     private static void OpenAndClose(string database) 
     { 
      Console.WriteLine("OpenAndClose enter, {0:N0}", stopWatch.ElapsedMilliseconds); 
      var connection = new SqlConnection(string.Format(@"Data Source=.\SQLEXPRESS;initial catalog={0};integrated security=SSPI", database)); 
      connection.Open(); 
      connection.Close(); 
      Console.WriteLine("OpenAndClose exit, {0:N0}", stopWatch.ElapsedMilliseconds); 
     } 
    } 
} 

내 컴퓨터에서 이것은 일관되게 서면으로 실패합니다. "master"와의 연결에는 문제가 없습니다. 다른 데이터베이스에 대한 연결 만. 연결 확인을 위해 연결 순서를 반대로 할 수 있습니다. Start() 메서드에서 Thread.Sleep의 주석을 제거하면 올바르게 작동합니다.

분명히 나는 ​​임의의 것을 피하고자한다. Thread.Sleep(). 랭크 코드 냄새 외에, 내가 임의의 가치를 부여 할 수 있을까요? 우리가 생각할 수있는 유일한 방법은 while 루프에서 대상 데이터베이스에 더미 연결을 설정하고 throw 된 SqlException을 catch 한 다음 작동 할 때까지 다시 시도하는 것입니다. 그러나 서비스가 실제로 사용될 준비가되었는지를 알기 위해보다 세련된 솔루션이 있어야한다고 생각합니다. 어떤 아이디어?

편집 : 아래에 제공된 피드백을 기반으로 데이터베이스의 상태를 확인했습니다. 그러나 여전히이 실패했습니다. 그것은 심지어 상태가 신뢰할 수없는 것처럼 보입니다.

private static void WaitForOnline(string database) 
{ 
    Console.WriteLine("WaitForOnline start, {0:N0}", stopWatch.ElapsedMilliseconds); 

    using (var connection = new SqlConnection(string.Format(@"Data Source=.\SQLEXPRESS;initial catal 
    using (var command = connection.CreateCommand()) 
    { 
     connection.Open(); 

     try 
     { 
      command.CommandText = "SELECT [state] FROM sys.databases WHERE [name] = @DatabaseName"; 
      command.Parameters.AddWithValue("@DatabaseName", database); 

      byte databaseState = (byte)command.ExecuteScalar(); 
      Console.WriteLine("databaseState = {0}", databaseState); 
      while (databaseState != OnlineState) 
      { 
       Thread.Sleep(500); 
       databaseState = (byte)command.ExecuteScalar(); 
       Console.WriteLine("databaseState = {0}", databaseState); 
      } 
     } 
     finally 
     { 
      connection.Close(); 
     } 
    } 

    Console.WriteLine("WaitForOnline exit, {0:N0}", stopWatch.ElapsedMilliseconds); 
} 

나는 비슷한 문제를 다루는 another discussion을 발견 : 여기 OpenAndClose (문자열) 전에 호출하고있는 기능입니다. 분명히 해결책은 해당 데이터베이스의 sys.database_files를 확인하는 것입니다. 그러나 그것은 물론 닭고기 및 달걀 문제입니다. 다른 아이디어?

답변

1

서비스 시작! = 데이터베이스 시작.

SQL Server 프로세스가 실행 중이고 '활성'인 SCM에 응답 할 때 서비스가 시작됩니다. 그 후에 서버는 사용자 데이터베이스를 온라인에 넣기 시작할 것입니다. 이 프로세스의 일부로 트랜잭션 일관성을 보장하기 위해 각 데이터베이스에서 복구 프로세스를 실행합니다. 데이터베이스 복구는 마이크로 초에서 하루 동안 지속될 수 있으며 다시 실행할 로그의 크기와 디스크의 속도에 따라 달라집니다.

SCM이 서비스가 실행 중임을 반환하면 'master'에 연결하고 sys.databases에서 데이터베이스 상태를 확인해야합니다. 상태가 ONLINE 인 경우에만 계속 열 수 있습니다.

+0

온라인에 대한 수표를 추가했습니다. 그것은 여전히 ​​실패하고 있습니다. : 필자는 원래의 게시물을 편집하고 sys_database_files에 대한 주석을 추가로 편집했습니다. –

+0

서비스 시작 후 마스터에 연결할 수 있으므로 대상 데이터베이스 상태를 확인하고 온라인 상태입니다 , 아직 초기 db 대상 데이터베이스를 지정하는 연결을 열려고 할 때 여전히 실패합니다. 오류가있는 오류가 있습니까? ADO.Net 연결 풀링이이 시나리오를 방해 할 수 있는지 이해하려고 해요 (shouldn 't,하지만 하나는 결코 알지 못합니다 ...) db가 ONLINE이라도 conenciton이 여전히 오류를 throw하면 실제로 시도가 성공할 때까지는 try/catch 루프 이외의 해결책이 표시되지 않습니다. –

관련 문제