我们需要编程访问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