2009-11-09 4 views
0

Nous avons besoin d'un accès par programmation à un service SQL Server Express dans le cadre de notre application. Selon ce que l'utilisateur essaie de faire, il se peut que nous devions joindre une base de données, détacher une base de données, en sauvegarder une, etc. Parfois, le service pourrait ne pas être démarré avant que nous n'essayions ces opérations. Nous devons donc nous assurer que le service est démarré. Voici où nous rencontrons des problèmes. Apparemment, le ServiceController.WaitForStatus(ServiceControllerStatus.Running) revient prématurément pour SQL Server Express. Ce qui est vraiment déroutant, c'est que la base de données principale semble être immédiatement disponible, mais pas d'autres bases de données. Voici une application console pour démontrer ce dont je parle:Quand un service démarré n'est-il pas un service démarré? (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); 
     } 
    } 
} 

Sur ma machine, cela ne fonctionne pas toujours comme écrit. Notez que la connexion à "maître" n'a aucun problème; seulement la connexion à l'autre base de données. (Vous pouvez inverser l'ordre des connexions pour le vérifier.) Si vous décommentez le Thread.Sleep dans la méthode Start(), cela fonctionnera correctement.

Il est évident que je veux éviter une Thread.Sleep() arbitraire. Outre l'odeur du code de rang, quelle valeur arbitraire aurais-je mise là? La seule chose à laquelle nous pouvons penser est de placer des connexions fictives dans notre base de données cible dans une boucle while, en interceptant l'exception SqlException et en essayant à nouveau jusqu'à ce que cela fonctionne. Mais je pense qu'il doit y avoir une solution plus élégante pour savoir quand le service est vraiment prêt à être utilisé. Des idées?

EDIT: Basé sur les commentaires fournis ci-dessous, j'ai ajouté une vérification de l'état de la base de données. Cependant, il reste défaillant. Il semble que même l'état n'est pas fiable. Voici la fonction que je fais appel avant 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); 
} 

J'ai trouvé another discussion face à un problème similaire. Apparemment, la solution consiste à vérifier les fichiers sys.database_files de la base de données en question. Mais cela, bien sûr, est un problème de poule et d'oeuf. D'autres idées?

Répondre

1

Démarrage du service! = Démarrage de la base de données.

service est démarré lorsque le processus SQL Server est en cours d'exécution et a répondu à la SCM qui est « vivant ». Après cela, le serveur commencera à mettre des bases de données utilisateur en ligne. Dans le cadre de ce processus, il exécute le processus de récupération sur chaque base de données, afin d'assurer la cohérence transactionnelle. La récupération d'une base de données peut durer de quelques microsecondes à des jours entiers, cela dépend de l'ammount du journal à refaire et de la vitesse du (des) disque (s).

Après le SCM retourne que le service est en cours d'exécution, vous devez vous connecter à « maître » et vérifier l'état de votre base de données dans sys.databases. Ce n'est que lorsque le statut est EN LIGNE que vous pouvez procéder à l'ouverture.

+0

J'ai ajouté le chèque pour ONLINE. Il échoue toujours. :( J'editted mon post original pour inclure le chèque, ainsi qu'un commentaire au sujet sys_database_files j'ai vu sur d'autres discussions. –

+0

Vous pouvez vous connecter à maîtriser service après le démarrage, vous vérifiez l'état de la base de données cible et est en ligne , et pourtant, lorsque vous essayez d'ouvrir une connexion qui spécifie le catalogue initial de la cible db, il échoue toujours.Est-il une erreur dans le ERRORLOG? J'essaie de comprendre si le regroupement de connexions ADO.Net peut interférer avec ce scénario (il devrait mais on ne sait jamais ...) Si même après que le db soit ONLINE la conenciton jette encore une erreur, je ne vois pas de solution autre que la boucle try/catch jusqu'à ce qu'elle réussisse réellement (beurk). –

Questions connexes