什么时候启动服务而不是启动服务? (SQL Express)

时间:2009-11-09 21:40:54

标签: service sql-server-express

我们需要以编程方式访问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。但这当然是一个鸡与蛋的问题。还有其他想法吗?

1 个答案:

答案 0 :(得分:1)

服务开始!=数据库启动。

服务在SQL Server进程运行时启动,并响应“活动”的SCM。之后,服务器将开始将用户数据库置于在线状态。作为此过程的一部分,它在每个数据库上运行恢复过程,以确保事务一致性。数据库的恢复可以持续从微秒到整天,这取决于要重做的日志量和磁盘的速度。

SCM返回服务正在运行后,您应该连接到“master”并检查sys.databases中的数据库状态。只有当状态为ONLINE时才能继续打开它。