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 à l’aide de l’outil de recherche.
En utilisant c #, j'aimerais lancer un fichier .sql. Le fichier SQL contient plusieurs instructions SQL, dont certaines sont réparties sur plusieurs lignes. J'ai essayé de lire dans le fichier et d'exécuter le fichier en utilisant ODP.NET ... Cependant, je ne pense pas qu'ExecuteNonQuery est vraiment conçu pour cela.
J'ai donc essayé d'utiliser sqlplus en créant un processus ... Cependant, à moins que je crée le processus avec UseShellExecute défini sur true, sqlplus se bloque et ne se ferme 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 = string.Format("xx/xx@{0} @{1}", in_database, s);
p.StartInfo.CreateNoWindow = true;
bool started = p.Start();
p.WaitForExit();
WaitForExit ne retourne jamais ... sauf si je règle 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)
{
string sqlConnectionString = @"Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=ccwebgrity;Data Source=SURAJIT\SQLEXPRESS";
string script = File.ReadAllText(@"E:\Project Docs\MX462-PD\MX756_ModMappings1.sql");
SqlConnection conn = new SqlConnection(sqlConnectionString);
Server server = new Server(new ServerConnection(conn));
server.ConnectionContext.ExecuteNonQuery(script);
}
}
J'ai essayé cette solution avec Microsoft.SqlServer.Management mais cela ne fonctionnait pas bien avec .NET 4.0, j'ai donc écrit une autre solution en utilisant uniquement le framework .NET libs.
string script = File.ReadAllText(@"E:\someSqlScript.sql");
// split script on GO command
IEnumerable<string> commandStrings = Regex.Split(script, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase);
Connection.Open();
foreach (string commandString in commandStrings)
{
if (commandString.Trim() != "")
{
using(var command = new SqlCommand(commandString, Connection))
{
command.ExecuteNonQuery();
}
}
}
Connection.Close();
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(string pathStoreProceduresFile, string connectionString)
{
try
{
string script = File.ReadAllText(pathStoreProceduresFile);
// split script on GO command
System.Collections.Generic.IEnumerable<string> commandStrings = Regex.Split(script, @"^\s*GO\s*$",
RegexOptions.Multiline | RegexOptions.IgnoreCase);
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
foreach (string commandString in commandStrings)
{
if (commandString.Trim() != "")
{
using (var command = new SqlCommand(commandString, connection))
{
try
{
command.ExecuteNonQuery();
}
catch (SqlException ex)
{
string spError = commandString.Length > 100 ? commandString.Substring(0, 100) + " ...\n..." : commandString;
MessageBox.Show(string.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;
}
}
Placez la commande pour exécuter le script SQL dans un fichier de commandes, puis exécutez le code ci-dessous.
string batchFileName = @"c:\batosql.bat";
string 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 )
écrivez quelque chose comme ceci dans le fichier de commandes (exemple pour serveur SQL)
osql -E -i %1
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();
string script = File.ReadAllText(Server.MapPath("~/Script/DatingDemo.sql"));
// split script on GO command
IEnumerable<string> commandStrings = Regex.Split(script, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase);
foreach (string commandString in commandStrings)
{
if (commandString.Trim() != "")
{
new SqlCommand(commandString, conn).ExecuteNonQuery();
}
}
lblmsg.Text = "Database updated successfully.";
}
catch (SqlException er)
{
lblmsg.Text = er.Message;
lblmsg.ForeColor = Color.Red;
}
finally
{
conn.Close();
}
}
Améliorations supplémentaires apportées à la réponse surajits:
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 connectionString = @"your-connection-string";
var pathToScriptFile = Server.MapPath("~/sql-scripts/") + "sql-script.sql";
var sqlScript = File.ReadAllText(pathToScriptFile);
using (var connection = new SqlConnection(connectionString))
{
var server = new Server(new ServerConnection(connection));
server.ConnectionContext.ExecuteNonQuery(sqlScript);
}
}
}
}
De plus, j'ai dû ajouter 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: s à utiliser car il y a plusieurs dossiers dans C:\Program Files\Microsoft SQL Server mais dans mon application, ces deux fonctionnent.
J'ai réussi à trouver la réponse en lisant le manuel :)
Cet extrait du MSDN
L'exemple de code évite une situation 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 flux redirigé. Le processus parent attendrait indéfiniment la fin du processus enfant. Le processus enfant attendrait indéfiniment que le parent lise à partir du flux StandardOutput complet.
Il existe un problème similaire lorsque vous lisez tout le texte des flux d'erreur standard et de sortie standard. Par exemple, le code C # suivant effectue une opération de lecture sur les deux flux.
Transforme le code en ceci;
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "sqlplus";
p.StartInfo.Arguments = string.Format("xxx/xxx@{0} @{1}", in_database, s);
bool started = p.Start();
// important ... read stream input before waiting for exit.
// this avoids deadlock.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);
if (p.ExitCode != 0)
{
Console.WriteLine( string.Format("*** Failed : {0} - {1}",s,p.ExitCode));
break;
}
Ce qui se termine maintenant correctement.
Il y a deux points à considérer.
1) Ce code source a fonctionné pour moi:
private static string Execute(string credentials, string scriptDir, string scriptFilename)
{
Process process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.WorkingDirectory = scriptDir;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.FileName = "sqlplus";
process.StartInfo.Arguments = string.Format("{0} @{1}", credentials, scriptFilename);
process.StartInfo.CreateNoWindow = true;
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return output;
}
J'ai défini le répertoire de travail sur le répertoire de script, de sorte que les sous-scripts du script fonctionnent également.
Appelez ça par exemple comme 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. Pour éviter les attaques par injection SQL, veillez à ne pas générer ce script en fonction des entrées de l’utilisateur ou à utiliser des paramètres de commande à cet effet (voir la surcharge de ExecuteSqlCommand acceptant des paramètres).
public static void ExecuteSqlScript(string sqlScript)
{
using (MyEntities dataModel = new MyEntities())
{
// split script on GO commands
IEnumerable<string> commands =
Regex.Split(
sqlScript,
@"^\s*GO\s*$",
RegexOptions.Multiline | RegexOptions.IgnoreCase);
foreach (string command in commands)
{
if (command.Trim() != string.Empty)
{
dataModel.Database.ExecuteSqlCommand(command);
}
}
}
}