2009-11-09 122 views
0

我们需要编程访问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。但那当然是鸡与鸡的问题。任何其他想法?

回答

1

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

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

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

+0

我添加了ONLINE的支票。它仍然失败。 :( 我编辑了我原来的帖子,包括检查,以及关于我在其他讨论中看到的有关sys_database_files的评论。 – 2009-11-10 20:05:13

+0

因此,您可以在服务启动后连接到主服务器,您正在检查目标数据库状态并处于ONLINE状态,但是当你尝试打开一个连接,指定初始目录的目标数据库,它仍然失败。是否有ERRORLOG错误?我试图了解ADO.Net连接池是否会干扰这种情况(它应该't,但是永远不会知道......)如果即使db在ONLINE之后,conenciton仍然会抛出错误,除非try/catch循环直到它成功(yuck),否则我没有看到任何解决方案。 – 2009-11-10 20:50:34