我们需要以编程方式访问SQL Server Express服务,作为我们应用程序的一部分。根据用户尝试执行的操作,我们可能必须附加数据库,分离数据库,备份数据库等。有时,在我们尝试这些操作之前,可能无法启动服务。所以我们需要确保服务开始。这是我们遇到问题的地方。显然,ServiceController.WaitForStatus(ServiceControllerStatus.Running)
过早地返回SQL Server Express。真正令人费解的是,master数据库似乎可以立即使用,而不是其他数据库。这是一个控制台应用程序来演示我在说什么:
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”的连接没有问题;只与其他数据库的连接。 (您可以颠倒连接的顺序以验证这一点。)如果您取消注释Thread.Sleep
方法中的Start()
,它将正常工作。
显然我想避免任意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。但这当然是一个鸡与蛋的问题。还有其他想法吗?
答案 0 :(得分:1)
服务开始!=数据库启动。
服务在SQL Server进程运行时启动,并响应“活动”的SCM。之后,服务器将开始将用户数据库置于在线状态。作为此过程的一部分,它在每个数据库上运行恢复过程,以确保事务一致性。数据库的恢复可以持续从微秒到整天,这取决于要重做的日志量和磁盘的速度。
SCM返回服务正在运行后,您应该连接到“master”并检查sys.databases中的数据库状态。只有当状态为ONLINE时才能继续打开它。