2009-11-09 184 views
0

我們需要編程訪問SQL Server Express服務作爲我們應用程序的一部分。根據用戶試圖執行的操作,我們可能需要附加數據庫,分離數據庫,備份數據庫等。有時,在我們嘗試這些操作之前,服務可能不會啓動。所以我們需要確保服務已經開始。這是我們遇到問題的地方。顯然,SQL Server Express過早返回ServiceController.WaitForStatus(ServiceControllerStatus.Running)。令人費解的是,master數據庫似乎是立即可用的,但不是其他數據庫。這裏是一個控制檯應用程序來演示我在說什麼:何時啓動的服務不是啓動的服務? (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); 
     } 
    } 
} 

在我的機器上,這將始終如故寫入。注意與「主」的連接沒有問題;只有連接到其他數據庫。 (您可以反轉連接的順序來驗證這一點。)如果取消註釋Start()方法中的Thread.Sleep,它將正常工作。

顯然我想避免任意Thread.Sleep()。除了等級代碼味道之外,我還會在那裏放置什麼樣的任意價值?我們唯一能想到的是在while循環中將一些虛連接放到我們的目標數據庫中,捕獲拋出的SqlException並再次嘗試,直到它工作。但我認爲必須有一個更優雅的解決方案來了解服務何時可以使用。有任何想法嗎?

編輯:根據下面提供的反饋,我添加了一個檢查數據庫的狀態。但是,這是仍然失敗。看起來即使這個狀態也不可靠。這裏是前OpenAndClose(串),我調用函數:

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處理類似的問題。顯然,解決方案是檢查有問題的數據庫的sys.database_files。但那當然是雞與雞的問題。任何其他想法?

回答

1

服務開始!=數據庫啓動。

服務在SQL Server進程正在運行時啓動並響應「活動」的SCM。之後,服務器將開始將用戶數據庫聯機。作爲此過程的一部分,它會在每個數據庫上運行恢復過程,以確保事務一致性。恢復數據庫可以持續幾微秒到整天,這取決於重做日誌的數量和磁盤的速度。

在SCM返回服務運行之後,您應該連接到'master'並檢查sys.databases中的數據庫狀態。只有狀態爲ONLINE時,才能繼續打開它。

+0

我添加了ONLINE的支票。它仍然失敗。 :( 我編輯了我原來的帖子,包括檢查,以及關於我在其他討論中看到的有關sys_database_files的評論。 – 2009-11-10 20:05:13

+0

因此,您可以在服務啓動後連接到主服務器,您正在檢查目標數據庫狀態並處於ONLINE狀態,但是當你嘗試打開一個連接,指定初始目錄的目標數據庫,它仍然失敗。是否有ERRORLOG錯誤?我試圖瞭解ADO.Net連接池是否會干擾這種情況(它應該't,但是永遠不會知道......)如果即使db在ONLINE之後,conenciton仍然會拋出錯誤,除非try/catch循環直到它成功(yuck),否則我沒有看到任何解決方案。 – 2009-11-10 20:50:34