Как выполнить файл сценария .SQL с помощью c #

140

Я уверен, что на этот вопрос уже был дан ответ, однако мне не удалось найти ответ с помощью инструмента поиска.

Используя С #, я хотел бы запустить файл .sql. Файл sql содержит несколько операторов sql, некоторые из которых разбиты на несколько строк. Я попытался прочитать файл и попытался выполнить файл с помощью ODP.NET ... однако я не думаю, что ExecuteNonQuery действительно предназначен для этого.

Поэтому я попытался использовать sqlplus, создав процесс ... однако, если я не запустил процесс с UseShellExecute, установленным в true, sqlplus зависнет и никогда не завершится. Вот код, который НЕ РАБОТАЕТ.

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 никогда не возвращает .... Если я не установил UseShellExecute в true. Побочный эффект UseShellExecute заключается в том, что вы не можете захватить перенаправленный вывод.

Богатый
источник
8
Здравствуйте, мистер Рич, ваш вопрос касался Oracle, и вы приняли решение, предназначенное для sql server? Вы изменили свою БД на sql server?
Akshay J

Ответы:

188
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);
    }
}
Каме
источник
5
Большой! Это решение помогло мне удалить и воссоздать базу данных, а также добавить таблицы (через указанный файл сценария SQL).
Ogre Psalm33
11
Этот метод не позволяет использовать команду «GO» в вашем скрипте, что разрешено при запуске скрипта из SQL Management Studio или команды osql. msdn.microsoft.com/en-us/library/ms188037.aspx
Rn222,
20
Rn222: Я думаю, вы запутали методы ExecuteNonQuery, SqlCommand.ExecuteNonQuery не позволяет использовать команды «GO», однако Server.ConnectionContext.ExecuteNonQuery определенно позволяет (я использую его прямо сейчас).
PeterBelm
44
Обратите внимание, что вам нужно добавить ссылки на проект, на Microsoft.SqlServer.ConnectionInfo, Microsoft.SqlServer.Management.Sdk и Microsoft.SqlServer.Smo, чтобы этот ответ работал.
thomasb
8
Для меня это не сработало при использовании .net 4.0 / 4.5 при обращении к 110 \ SDK \ Assemblies. Я нашел решение изменить app.Config на<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> </startup>
Abir
107

Я пробовал это решение с Microsoft.SqlServer.Management, но оно не работало с .NET 4.0, поэтому я написал другое решение, используя только платформу .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 (!string.IsNullOrWhiteSpace(commandString.Trim()))
    {
        using(var command = new SqlCommand(commandString, Connection))
        {
            command.ExecuteNonQuery();
        }
    }
}     
Connection.Close();
Hacko
источник
В яблочко. Это решение даже не закроет файл после его использования. Это может быть критично.
Матиас Ликкегаард Лоренцен
1
Используйте «RegexOptions.Multiline | RegexOptions.IgnoreCase», чтобы соответствовать случаям «Go» или «Go».
Анкуш
1
Я думаю, что следует использовать флаг RegexOptions.CultureInvariant.
Дэйв Андерсен
3
Это не работает на 100%: GO может принимать числовой параметр.
nothrow
Если после GO есть комментарий, он попытается выполнить его как команду.
clamchoda
17

Это работает на Framework 4.0 или выше. Поддерживает «GO». Также покажите сообщение об ошибке, строку и команду 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;
        }
    }
Xtian11
источник
3
Хороший код, один очень незначительная вещь в том , что не нужно connection.Close()соединение будет закрыто usingвы обвил его.
Дружная
8

Поместите команду для выполнения сценария sql в пакетный файл, затем запустите приведенный ниже код

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 )

в командном файле напишите что-то вроде этого (образец для sql server)

osql -E -i %1
Биной ​​Антоний
источник
6

Это работает для меня:

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();
    }
}
Нилам сайни
источник
4

Добавлены дополнительные улучшения в ответ сураджитов:

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);
            }
        }
    }
}

Также мне пришлось добавить в свой проект следующие ссылки:

  • 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

Я понятия не имею, подходят ли эти dll: s для использования, поскольку в C: \ Program Files \ Microsoft SQL Server есть несколько папок, но в моем приложении эти две работают.

Кот в сапогах
источник
Это сработало для меня в .Net 4.7. Мне не нужны были другие DLL, упомянутые surajit. Однако мне пришлось использовать версию 13.0.0.0 как для Microsoft.SqlServer.ConnectionInfo, так и для Microsoft.SqlServer.Smo, поскольку 13.100.0.0 генерировал исключения при создании экземпляра ServerConnection.
Кевин Фихтер
4

Мне удалось найти ответ, прочитав инструкцию :)

Этот отрывок из MSDN

Пример кода позволяет избежать состояния взаимоблокировки, вызывая p.StandardOutput.ReadToEnd перед p.WaitForExit. Состояние взаимоблокировки может возникнуть, если родительский процесс вызывает p.WaitForExit перед p.StandardOutput.ReadToEnd, а дочерний процесс записывает достаточно текста для заполнения перенаправленного потока. Родительский процесс будет бесконечно ждать завершения дочернего процесса. Дочерний процесс будет бесконечно ждать, пока родительский процесс не прочитает полный поток StandardOutput.

Аналогичная проблема возникает, когда вы читаете весь текст как из стандартного потока вывода, так и из стандартного потока ошибок. Например, следующий код C # выполняет операцию чтения в обоих потоках.

Превращает код в это;

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;
}

Что теперь выходит правильно.

Богатый
источник
2
Совет относительно sqlplus: если вы хотите знать, было ли выполнение скрипта успешным, вы можете добавить WHENEVER SQLERROR EXIT SQL.SQLCODE в начало скрипта. Таким образом, процесс sqlplus возвращает номер ошибки sql в качестве кода возврата.
devdimi
какой-нибудь полный образец исходного кода? что такое in_database, s ??
Kiquenet 04
2
это не работает для меня. p.StandardOutput.ReadToEnd();никогда не выходит
Луис Рис
2

Следует учитывать два момента.

1) Этот исходный код работал у меня:

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;
}

Я установил рабочий каталог в каталог сценария, чтобы вложенные сценарии в сценарии также работали.

Назовите это, например, как Execute("usr/pwd@service", "c:\myscripts", "script.sql")

2) Вы должны завершить свой SQL-скрипт с помощью оператора EXIT;

StefanG
источник
1

Используя EntityFramework, вы можете пойти с таким решением. Я использую этот код для инициализации тестов e2e. Для предотвращения атак с использованием sql-инъекций убедитесь, что этот сценарий не создается на основе пользовательского ввода, и не используйте для этого параметры команды (см. Перегрузку ExecuteSqlCommand, которая принимает параметры).

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);
            }
        }              
    }
}
мартинос
источник
-1

Я не мог найти точного и действенного способа сделать это. Итак, после целого дня я пришел с этим смешанным кодом, полученным из разных источников и пытающимся выполнить свою работу.

Но он по-прежнему генерирует исключение, ExecuteNonQuery: CommandText property has not been Initializedдаже если он успешно запускает файл сценария - в моем случае он успешно создает базу данных и вставляет данные при первом запуске.

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
        {

        }

    }

}
Мухаммад Салман
источник
Я не мог найти точного и действенного способа сделать это. Итак, после целого дня я пришел с этим смешанным кодом, полученным из разных источников и пытающимся выполнить свою работу. поэтому я объединил их все и сделал результат. Но он все еще генерирует исключение «ExecuteNonQuery: свойство CommandText не было инициализировано». Хотя он успешно запускает файл сценария (в моем случае успешно создать базу данных и вставить данные при первом запуске).
Мухаммад Салман