Récemment j’ai répondu à une question sur StackOverflow. La question est comment exécuter un batch SQL contenant des “GO” en C# ? Par exemple comment exécuter ce batch :

SET ANSI_NULLS ON     
GO    
SET QUOTED_IDENTIFIER ON     
GO    
ALTER PROCEDURE [dbo].[Company_LoadAll]     
AS     
SET NOCOUNT ON     
SELECT  [Company].[Company_Id]     
FROM [Company]    
RETURN     
GO

Lorsque l’on exécute ce script via SQL Server Management Studio, il n’y a aucun problème. Cependant depuis une SqlCommand, l’exception System. Data. SqlClient. SqlException avec le message “Incorrect syntax near ‘GO’” est lancée.

Incorrect syntax near 'GO

L’idée est de découper le script à chaque “GO” et d’exécuter les commandes une par une. Cela parait simple (string. Split) mais le langage SQL est complexe et il y a beaucoup de cas à gérer. Par exemple les commentaires multi-lignes peuvent contenir des “GO” qui ne sont pas des séparateurs de commandes :

/*    
GO    
*/

ScriptDom

Ecrire un parser T-SQL complet est une tâche très fastidieuse, mais Microsoft en fournit un :). Après avoir référencé “Microsoft. SqlServer. TransactSql. ScriptDom” :

using (TextReader reader = new StringReader(script))     
{  
   TSqlParser parser = new TSql110Parser(true);     
   IList<ParseError> errors;     
   TSqlFragment fragment = parser.Parse(reader, out errors);     
   if (errors != null && errors.Count > 0)     
   {     
   foreach (ParseError error in errors)     
   {     
   Console.WriteLine("Line: {0}, Column: {1}: {2}", error.Line, error.Column, error.Message);     
   return;     
   }     
   }    
   SqlScriptGenerator sqlScriptGenerator = new Sql110ScriptGenerator();    
   TSqlScript sqlScript = fragment as TSqlScript;     
   if (sqlScript == null)     
   {     
   sqlScriptGenerator.GenerateScript(fragment, Console.Out); // TODO Exécuter le script     
   }     
   else     
   {     
   foreach (var sqlBatch in sqlScript.Batches)     
   {     
   Console.WriteLine("-- ");     
   sqlScriptGenerator.GenerateScript(sqlBatch, Console.Out); // TODO Exécuter le script     
   }     
   }     
}

A noter que pour d’autres besoin il est possible de parcourir le contenu du script à l’aide d’un visiteur (TSqlFragmentVisitor). Le seul point génant avec cette méthode est que les commentaires sont supprimés.

CodeFluent. Runtime. Database

CodeFluent.Runtime.Database permet également d’exécuter ce genre de script. Tous les cas bizarres ne sont pas pris en compte mais en général cela fonctionne bien :

using (StatementReader statementReader = new CodeFluent.Runtime.Database.Management.StatementReader("GO", Environment.NewLine, inputStream))     
{     
   Statement statement;     
   while ((statement = statementReader.Read(StatementReaderOptions.Default)) != null)     
   {     
   Console.WriteLine("-- ");     
   Console.WriteLine(statement.Command);     
   }     
}

Le code est court et non spécifique à SQL Server. Il est tout à fait possible d’utiliser un séparateur autre que « GO ». Il faut pour cela remplacer le premier paramètre du constructeur de « StatementReader ».

SQL Server Management Objects

Une dernière piste est d’utiliser SQL Server Management Objects (SMO). Premièrement il faut ajouter les références suivantes :

  • Microsoft. SqlServer. Smo
  • Microsoft. SqlServer. ConnectionInfo
  • Microsoft. SqlServer. Management. Sdk. Sfc

Ensuite ces quelques lignes suffisent :

using (SqlConnection connection = new SqlConnection("Server=(local);Database=Sample;Trusted_Connection=True;"))     
{     
   ServerConnection svrConnection = new ServerConnection(connection);     
   Server server = new Server(svrConnection);     
   server.ConnectionContext.ExecuteNonQuery(script);     
}

Ne ratez plus aucune actualité avec la newsletter mensuelle de SoftFluent

Newsletter SoftFluent