151 votes

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

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

En utilisant C#, je voudrais exécuter un fichier .sql. Ce fichier contient plusieurs instructions sql, dont certaines sont réparties sur plusieurs lignes. J'ai essayé de lire le fichier et d'exécuter le fichier à l'aide d'ODP.NET ... mais je ne pense pas que ExecuteNonQuery soit vraiment conçu pour cela.

J'ai donc essayé d'utiliser sqlplus en créant un processus ... mais à moins que je ne crée le processus avec UseShellExecute réglé sur true, sqlplus se bloque et ne sort 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 renvoie 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.

9 votes

Bonjour M. Rich, votre question concernait Oracle et vous avez accepté une solution qui était pour sql server ? Vous avez changé votre DB pour sql server ?

4voto

Rich Points 1123

J'ai réussi à trouver la réponse en lisant le manuel :)

Cet extrait du MSDN

L'exemple de code permet d'éviter un blocage en appelant p.StandardOutput.ReadToEnd avant p.WaitForExit. Une situation de blocage peut se produire si le processus parent appelle p.WaitForExit avant p.StandardOutput.ReadToEnd et que le processus enfant écrit suffisamment de texte pour pour remplir le flux redirigé. Le processus parent parent attendrait indéfiniment que le processus que le processus enfant se termine. Le processus enfant attendrait indéfiniment que le processus parent lise le flux complet StandardOutput.

Il y a un problème similaire lorsque vous lisez tout le texte à partir de la sortie standard et du flux d'erreur standard. Pour exemple, le code C# suivant 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;
}

Qui se termine maintenant correctement.

2 votes

Un conseil concernant sqlplus : si vous voulez savoir si l'exécution du script a réussi, vous pouvez ajouter WHENEVER SQLERROR EXIT SQL.SQLCODE au début du script. De cette façon, le processus sqlplus renvoie le numéro d'erreur sql comme code de retour.

0 votes

Un échantillon complet du code source ? qu'est-ce que in_database, s ? ?

2 votes

Ça ne marche pas pour moi. p.StandardOutput.ReadToEnd(); ne sort jamais

4voto

PussInBoots Points 1129

Ajouté des améliorations supplémentaires à la réponse de 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 à utiliser car il y a plusieurs dossiers dans le dossier C:\Program Fichiers \Microsoft SQL Server mais dans mon application ces deux-là fonctionnent.

0 votes

Cela a fonctionné pour moi dans .Net 4.7. Je n'avais pas besoin des autres dlls mentionnées par surajit. Cependant, j'ai dû utiliser la version 13.0.0.0 pour Microsoft.SqlServer.ConnectionInfo et Microsoft.SqlServer.Smo, car la version 13.100.0.0 générait des exceptions lors de l'instanciation de ServerConnection.

2voto

StefanG Points 632

Il y a deux points à prendre en compte.

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 dans le répertoire script, de sorte que les sous-scripts dans le script fonctionnent également.

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

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

1voto

martinoss Points 154

En utilisant EntityFramework, vous pouvez opter pour une solution comme celle-ci. J'utilise ce code pour initialiser les tests e2e. Pour prévenir les attaques par injection sql, assurez-vous de ne pas générer ce script basé sur l'entrée de l'utilisateur ou d'utiliser des paramètres de commande pour cela (voir la surcharge de ExecuteSqlCommand qui accepte les 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);
            }
        }              
    }
}

0voto

Muhammad Salman Points 11

Je n'ai pas trouvé de méthode exacte et valable pour le faire. Ainsi, après une journée entière, je suis arrivé avec ce code mixte réalisé à partir de différentes sources et j'ai essayé de faire le travail.

Mais il génère toujours une exception ExecuteNonQuery: CommandText property has not been Initialized même s'il exécute avec succès le fichier script - dans mon cas, il crée avec succès la base de données et insère les données au premier démarrage.

public partial class Form1 : MetroForm
{
    SqlConnection cn;
    SqlCommand cm;
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        if (!CheckDatabaseExist())
        {
            GenerateDatabase();
        }
    }

    private bool CheckDatabaseExist()
    {
        SqlConnection con = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=SalmanTradersDB;Integrated Security=true");
        try
        {
            con.Open();
            return true;
        }
        catch
        {
            return false;
        }
    }

    private void GenerateDatabase()
    {

        try
        {
            cn = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=master;Integrated Security=True");
            StringBuilder sb = new StringBuilder();
            sb.Append(string.Format("drop databse {0}", "SalmanTradersDB"));
            cm = new SqlCommand(sb.ToString() , cn);
            cn.Open();
            cm.ExecuteNonQuery();
            cn.Close();
        }
        catch
        {

        }
        try
        {
            //Application.StartupPath is the location where the application is Installed
            //Here File Path Can Be Provided Via OpenFileDialog
            if (File.Exists(Application.StartupPath + "\\script.sql"))
            {
                string script = null;
                script = File.ReadAllText(Application.StartupPath + "\\script.sql");
                string[] ScriptSplitter = script.Split(new string[] { "GO" }, StringSplitOptions.None);
                using (cn = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=master;Integrated Security=True"))
                {
                    cn.Open();
                    foreach (string str in ScriptSplitter)
                    {
                        using (cm = cn.CreateCommand())
                        {
                            cm.CommandText = str;
                            cm.ExecuteNonQuery();
                        }
                    }
                }
            }
        }
        catch
        {

        }

    }

}

0 votes

Je n'ai pas trouvé de méthode exacte et valable pour le faire. Ainsi, après une journée entière, je suis arrivé avec ce code mixte obtenu à partir de différentes sources et essayant de faire le travail. Je les ai donc tous fusionnés et j'ai obtenu le résultat. Mais il génère toujours une exception "ExecuteNonQuery : La propriété CommandText n'a pas été initialisée." Bien qu'il exécute avec succès le fichier script(Dans mon cas, créer avec succès la base de données et insérer des données au premier démarrage).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X