Когда запущенная служба не запущена? (SQL Express) - PullRequest
0 голосов
/ 10 ноября 2009

Нам требуется программный доступ к службе SQL Server Express как части нашего приложения. В зависимости от того, что пытается сделать пользователь, нам может потребоваться присоединить базу данных, отсоединить базу данных, создать резервную копию и т. Д. Иногда служба может не запускаться до того, как мы попытаемся выполнить эти операции. Поэтому нам нужно убедиться, что сервис запущен. Здесь мы сталкиваемся с проблемами. Очевидно, ServiceController.WaitForStatus(ServiceControllerStatus.Running) преждевременно возвращается для SQL Server 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);
        }
    }
}

На моей машине это не получится, как написано. Обратите внимание, что подключение к «мастеру» не имеет проблем; только соединение с другой базой данных. (Вы можете изменить порядок соединений, чтобы убедиться в этом.) Если вы раскомментируете Thread.Sleep в методе Start(), он будет работать нормально.

Очевидно, я хочу избежать произвольного Thread.Sleep(). Помимо запаха рангового кода, какое произвольное значение я бы там поставил? Единственное, о чем мы можем подумать, - это поместить несколько фиктивных соединений с нашей целевой базой данных в цикл while, перехватить сгенерированное SqlException и повторить попытку, пока он не заработает. Но я думаю, что должно быть более элегантное решение, чтобы знать, когда служба действительно готова к использованию. Есть идеи?

РЕДАКТИРОВАТЬ: На основе обратной связи, представленной ниже, я добавил проверку состояния базы данных. Тем не менее, все еще терпит неудачу. Похоже, даже государство ненадежно. Вот функция, которую я вызываю перед OpenAndClose (string):

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);
}

Я нашел другое обсуждение , посвященное аналогичной проблеме. По-видимому, решение состоит в том, чтобы проверить файлы sys.database_files рассматриваемой базы данных. Но это, конечно, проблема курицы и яйца. Есть еще идеи?

1 Ответ

1 голос
/ 10 ноября 2009

Запуск службы! = Запуск базы данных.

Служба запускается, когда процесс SQL Server запущен и отвечает на «живой» SCM. После этого сервер начнет размещать пользовательские базы данных в сети. В рамках этого процесса он запускает процесс восстановления для каждой базы данных, чтобы обеспечить согласованность транзакций. Восстановление базы данных может длиться от микросекунд до целых дней, это зависит от количества журналов, которые нужно восстановить, и скорости диска (ов).

После того, как SCM вернет, что служба работает, вы должны подключиться к «master» и проверить состояние вашей базы данных в sys.databases. Только когда он находится в режиме онлайн, вы можете открыть его.

...