Comment exécuter un fichier de script .SQL en utilisant c #

Je suis sûr que cette question a déjà reçu une réponse, mais je n’ai pas pu trouver de réponse en utilisant l’outil de recherche.

En utilisant c #, je voudrais lancer un fichier .sql. Le fichier sql contient plusieurs instructions SQL, dont certaines sont réparties sur plusieurs lignes. J’ai essayé de lire le fichier et j’ai essayé d’exécuter le fichier en utilisant ODP.NET … mais je ne pense pas qu’ExecuteNonQuery est vraiment conçu pour cela.

Donc, j’ai essayé d’utiliser sqlplus via un processus de création … Cependant, à moins d’avoir généré le processus avec UseShellExecute défini sur true, sqlplus se bloquerait et ne quitterait jamais. Voici le code qui NE FONCTIONNE PAS.

Process p = new Process(); p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.FileName = "sqlplus"; p.StartInfo.Arguments = ssortingng.Format("xx/xx@{0} @{1}", in_database, s); p.StartInfo.CreateNoWindow = true; bool started = p.Start(); p.WaitForExit(); 

WaitForExit ne retourne jamais …. À moins que je définisse UseShellExecute sur true. Un effet secondaire de UseShellExecute est que vous ne pouvez pas capturer la sortie redirigée.

 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Common; using System.IO; using System.Data.SqlClient; public partial class ExcuteScript : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { ssortingng sqlConnectionSsortingng = @"Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=ccwebgrity;Data Source=SURAJIT\SQLEXPRESS"; ssortingng script = File.ReadAllText(@"E:\Project Docs\MX462-PD\MX756_ModMappings1.sql"); SqlConnection conn = new SqlConnection(sqlConnectionSsortingng); Server server = new Server(new ServerConnection(conn)); server.ConnectionContext.ExecuteNonQuery(script); } } 

J’ai essayé cette solution avec Microsoft.SqlServer.Management mais celle-ci ne fonctionnait pas bien avec .NET 4.0. J’ai donc écrit une autre solution en utilisant uniquement le framework libs .NET.

  ssortingng script = File.ReadAllText(@"E:\someSqlScript.sql"); // split script on GO command IEnumerable commandSsortingngs = Regex.Split(script, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase); Connection.Open(); foreach (ssortingng commandSsortingng in commandSsortingngs) { if (commandSsortingng.Trim() != "") { using(var command = new SqlCommand(commandSsortingng, Connection)) { command.ExecuteNonQuery(); } } } Connection.Close(); 

Placez la commande pour exécuter le script SQL dans un fichier de commandes, puis exécutez le code ci-dessous

 ssortingng batchFileName = @"c:\batosql.bat"; ssortingng sqlFileName = @"c:\MySqlScripts.sql"; Process proc = new Process(); proc.StartInfo.FileName = batchFileName; proc.StartInfo.Arguments = sqlFileName; proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; proc.StartInfo.ErrorDialog = false; proc.StartInfo.WorkingDirectory = Path.GetDirectoryName(batchFileName); proc.Start(); proc.WaitForExit(); if ( proc.ExitCode!= 0 ) 

dans le fichier de commandes, écrivez quelque chose comme ceci (exemple pour SQL Server)

 osql -E -i %1 

Cela fonctionne sur Framework 4.0 ou supérieur. Prend en charge “GO”. Affiche également le message d’erreur, la ligne et la commande sql.

 using System.Data.SqlClient; private bool runSqlScriptFile(ssortingng pathStoreProceduresFile, ssortingng connectionSsortingng) { try { ssortingng script = File.ReadAllText(pathStoreProceduresFile); // split script on GO command System.Collections.Generic.IEnumerable commandSsortingngs = Regex.Split(script, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase); using (SqlConnection connection = new SqlConnection(connectionSsortingng)) { connection.Open(); foreach (ssortingng commandSsortingng in commandSsortingngs) { if (commandSsortingng.Trim() != "") { using (var command = new SqlCommand(commandSsortingng, connection)) { try { command.ExecuteNonQuery(); } catch (SqlException ex) { ssortingng spError = commandSsortingng.Length > 100 ? commandSsortingng.Subssortingng(0, 100) + " ...\n..." : commandSsortingng; MessageBox.Show(ssortingng.Format("Please check the SqlServer script.\nFile: {0} \nLine: {1} \nError: {2} \nSQL Command: \n{3}", pathStoreProceduresFile, ex.LineNumber, ex.Message, spError), "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); return false; } } } } connection.Close(); } return true; } catch (Exception ex) { MessageBox.Show(ex.Message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); return false; } } 

Améliorations supplémentaires apscopes aux surajits répondent:

 using System; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Common; using System.IO; using System.Data.SqlClient; namespace MyNamespace { public partial class RunSqlScript : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { var connectionSsortingng = @"your-connection-ssortingng"; var pathToScriptFile = Server.MapPath("~/sql-scripts/") + "sql-script.sql"; var sqlScript = File.ReadAllText(pathToScriptFile); using (var connection = new SqlConnection(connectionSsortingng)) { var server = new Server(new ServerConnection(connection)); server.ConnectionContext.ExecuteNonQuery(sqlScript); } } } } 

De plus, j’ai dû append les références suivantes à mon projet:

  • C:\Program Files\Microsoft SQL Server\120\SDK\Assemblies\Microsoft.SqlServer.ConnectionInfo.dll
  • C:\Program Files\Microsoft SQL Server\120\SDK\Assemblies\Microsoft.SqlServer.Smo.dll

Je n’ai aucune idée si ce sont les bonnes DLL à utiliser car il existe plusieurs dossiers dans C: \ Program Files \ Microsoft SQL Server mais dans mon application ces deux travaux.

J’ai réussi à trouver la réponse en lisant le manuel 🙂

Cet extrait de MSDN

L’exemple de code évite une condition de blocage en appelant p.StandardOutput.ReadToEnd avant p.WaitForExit. Une condition de blocage peut survenir si le processus parent appelle p.WaitForExit avant p.StandardOutput.ReadToEnd et que le processus enfant écrit suffisamment de texte pour remplir le stream redirigé. Le processus parent attend indéfiniment la fin du processus enfant. Le processus enfant attend indéfiniment que le parent lise à partir du stream StandardOutput complet.

Il y a un problème similaire lorsque vous lisez tout le texte à la fois à partir des stream de sortie standard et d’erreur standard. Par exemple, le code C # suivant effectue une opération de lecture sur les deux stream.

Transforme le code en ceci;

 Process p = new Process(); p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.FileName = "sqlplus"; p.StartInfo.Arguments = ssortingng.Format("xxx/xxx@{0} @{1}", in_database, s); bool started = p.Start(); // important ... read stream input before waiting for exit. // this avoids deadlock. ssortingng output = p.StandardOutput.ReadToEnd(); p.WaitForExit(); Console.WriteLine(output); if (p.ExitCode != 0) { Console.WriteLine( ssortingng.Format("*** Failed : {0} - {1}",s,p.ExitCode)); break; } 

Ce qui sort maintenant correctement.

Cela fonctionne pour moi:

 public void updatedatabase() { SqlConnection conn = new SqlConnection("Data Source=" + txtserver.Text.Trim() + ";Initial Catalog=" + txtdatabase.Text.Trim() + ";User ID=" + txtuserid.Text.Trim() + ";Password=" + txtpwd.Text.Trim() + ""); try { conn.Open(); ssortingng script = File.ReadAllText(Server.MapPath("~/Script/DatingDemo.sql")); // split script on GO command IEnumerable commandSsortingngs = Regex.Split(script, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase); foreach (ssortingng commandSsortingng in commandSsortingngs) { if (commandSsortingng.Trim() != "") { new SqlCommand(commandSsortingng, conn).ExecuteNonQuery(); } } lblmsg.Text = "Database updated successfully."; } catch (SqlException er) { lblmsg.Text = er.Message; lblmsg.ForeColor = Color.Red; } finally { conn.Close(); } } 

Il y a deux points à considérer.

1) Ce code source a fonctionné pour moi:

 private static ssortingng Execute(ssortingng credentials, ssortingng scriptDir, ssortingng scriptFilename) { Process process = new Process(); process.StartInfo.UseShellExecute = false; process.StartInfo.WorkingDirectory = scriptDir; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.FileName = "sqlplus"; process.StartInfo.Arguments = ssortingng.Format("{0} @{1}", credentials, scriptFilename); process.StartInfo.CreateNoWindow = true; process.Start(); ssortingng output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); return output; } 

Je mets le répertoire de travail dans le répertoire de script, de sorte que les sous-scripts du script fonctionnent également.

Appelez-le par exemple Execute("usr/pwd@service", "c:\myscripts", "script.sql")

2) Vous devez finaliser votre script SQL avec l’instruction EXIT;

En utilisant EntityFramework, vous pouvez utiliser une solution comme celle-ci. J’utilise ce code pour initialiser les tests e2e. Prévenez les attaques par injection SQL, assurez-vous de ne pas générer ce script en fonction des entrées utilisateur ou utilisez les parameters de commande pour cela (voir la surcharge d’ExecuteSqlCommand qui accepte les parameters).

 public static void ExecuteSqlScript(ssortingng sqlScript) { using (MyEntities dataModel = new MyEntities()) { // split script on GO commands IEnumerable commands = Regex.Split( sqlScript, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase); foreach (ssortingng command in commands) { if (command.Trim() != ssortingng.Empty) { dataModel.Database.ExecuteSqlCommand(command); } } } }