我們需要編程訪問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。但那當然是雞與雞的問題。任何其他想法?
我添加了ONLINE的支票。它仍然失敗。 :( 我編輯了我原來的帖子,包括檢查,以及關於我在其他討論中看到的有關sys_database_files的評論。 – 2009-11-10 20:05:13
因此,您可以在服務啓動後連接到主服務器,您正在檢查目標數據庫狀態並處於ONLINE狀態,但是當你嘗試打開一個連接,指定初始目錄的目標數據庫,它仍然失敗。是否有ERRORLOG錯誤?我試圖瞭解ADO.Net連接池是否會干擾這種情況(它應該't,但是永遠不會知道......)如果即使db在ONLINE之後,conenciton仍然會拋出錯誤,除非try/catch循環直到它成功(yuck),否則我沒有看到任何解決方案。 – 2009-11-10 20:50:34