C # kullanarak bir .SQL komut dosyası yürütme


140

Eminim bu soru zaten cevaplanmıştır, ancak arama aracını kullanarak bir cevap bulamadım.

C # kullanarak bir .sql dosyası çalıştırmak istiyorum. Sql dosyası, bazıları birden çok satıra bölünmüş birden fazla sql ifadesi içerir. Dosyada okuma denedim ve ODP.NET kullanarak dosyayı yürütmeye çalıştım ... Ancak ben ExecuteNonQuery gerçekten bunu yapmak için tasarlanmış olduğunu sanmıyorum.

Bu yüzden bir süreç yumurtlama yoluyla sqlplus kullanmayı denedim ... Ancak sürece UseShellExecute gerçek sqlplus için ayarlanmış olan süreç yumurtlama asmak ve asla çıkmak. İşte ÇALIŞMAYAN kod.

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 hiçbir zaman geri dönmez .... UseShellExecute öğesini true olarak ayarlamazsam. UseShellExecute öğesinin bir yan etkisi, yeniden yönlendirilen çıktıyı yakalayamamanızdır.


8
Merhaba Bay Rich, sorunuz Oracle ile ilgiliydi ve sql sunucusu için olan bir çözümü kabul ettiniz mi? DB'nizi sql sunucusuna mı değiştirdiniz?
Akshay J

Yanıtlar:


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

4
Harika! Bu çözüm, bir veritabanını bırakıp yeniden oluşturabildiğim ve tablolar eklediğim için (başvurulan SQL komut dosyası aracılığıyla) benim için çalıştı.
Ogre Psalm33

11
Bu yöntem, komut dosyanızda SQL Management Studio'dan veya osql komutundan bir komut dosyası çalıştırdığınızda izin verilen "GO" komutunun kullanılmasına izin vermez. msdn.microsoft.com/en-us/library/ms188037.aspx
Rn222

20
Rn222: Bence ExecuteNonQuery yöntemlerini karıştırdınız, SqlCommand.ExecuteNonQuery "GO" komutlarının kullanılmasına izin vermiyor, ancak Server.ConnectionContext.ExecuteNonQuery kesinlikle kullanıyor (Şu anda kullanıyorum).
PeterBelm

44
Bu yanıtın çalışması için Microsoft.SqlServer.ConnectionInfo, Microsoft.SqlServer.Management.Sdk ve Microsoft.SqlServer.Smo projelerine başvurular eklemeniz gerektiğini unutmayın.
thomasb

8
Bana göre .net 4.0 / 4.5 kullanırken, 110 \ SDK \ Assemblies'a başvururken işe yaramadı Bulduğum çözüm, uygulamayı değiştiriyordu.Config<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> </startup>
Abir

107

Bu çözümü Microsoft.SqlServer.Management ile denedim ama .NET 4.0 ile iyi çalışmadı, bu yüzden sadece .NET libs çerçevesini kullanarak başka bir çözüm yazdım.

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

Kesinlikle. Bu çözüm, dosyayı kullandıktan sonra dosyayı kapatmayacaktır. Bu kritik olabilir.
Mathias Lykkegaard Lorenzen

1
"Git" veya "git" vakalarını da eşleştirmek için "RegexOptions.Multiline | RegexOptions.IgnoreCase" kullanın.
Ankush

1
Bence RegexOptions.CultureInvariant bayrağı da kullanılmalı.
Dave Andersen

3
Bu% 100 çalışmıyor: 'GO' sayısal parametreyi kabul edebilir.
nothrow

16

Bu, Framework 4.0 veya Üzeri üzerinde çalışır. "GO" destekler. Hata mesajını, satırı ve sql komutunu da gösterin.

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

3
Güzel bir kod, çok küçük bir şey, connection.Close()bağlandığınızda bağlantıya ihtiyaç duymayacağı using.
Dostça

Harika iş. Benim için 'kutunun dışında' çalıştı.
Stephen85

8

Sql komut dosyasını bir toplu iş dosyasına yürütme komutunu koyun ve aşağıdaki kodu çalıştırın

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 )

toplu iş dosyasına böyle bir şey yazın (sql sunucusu için örnek)

osql -E -i %1

6

Bu benim için çalışıyor:

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

Surajits cevabına ek iyileştirmeler eklendi:

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

Ayrıca, projeme aşağıdaki referansları eklemek zorunda kaldım:

  • 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

Bu C: \ Program Files \ Microsoft SQL Server'da birkaç klasör olduğu için kullanmak için doğru dll: s olup olmadığını bilmiyorum ama benim uygulamada bu iki çalışır.


Bu benim için .Net 4.7'de çalıştı. Surajit tarafından belirtilen diğer dll'lere ihtiyacım yoktu. Ancak, 13.100.0.0 ServerConnection somutlaştırırken istisnalar attı gibi, hem Microsoft.SqlServer.ConnectionInfo ve Microsoft.SqlServer.Smo için 13.0.0.0 sürümünü kullanmak zorunda kaldı.
Kevin Fichter

4

El kitabını okuyarak cevabı bulmayı başardım :)

MSDN'den alınan bu alıntı

Kod örneği, p.WaitForExit'ten önce p.StandardOutput.ReadToEnd öğesini çağırarak kilitlenme koşulunu önler. Üst işlem p.StandardOutput.ReadToEnd öğesinden önce p.WaitForExit öğesini çağırırsa ve alt işlem, yeniden yönlendirilen akışı doldurmak için yeterli metin yazarsa bir kilitlenme koşulu oluşabilir. Üst süreç süresiz olarak alt sürecin çıkmasını beklerdi. Alt süreç, üst öğenin tam StandardOutput akışından okumasını süresiz olarak bekler.

Hem standart çıktıdan hem de standart hata akışlarından tüm metni okuduğunuzda da benzer bir sorun vardır. Örneğin, aşağıdaki C # kodu her iki akışta bir okuma işlemi gerçekleştirir.

Kodu buna dönüştürür;

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

Hangi şimdi doğru çıkar.


2
Sqlplus ile ilgili bir ipucu: komut dosyasının yürütülmesinin başarılı olup olmadığını öğrenmek istiyorsanız, komut dosyasının başına WHENEVER SQLERROR EXIT SQL.SQLCODE ekleyebilirsiniz. Bu şekilde sqlplus işlemi, sql hata numarasını dönüş kodu olarak döndürür.
devdimi

herhangi bir tam kaynak kodu örneği? in_database nedir, s ??
Kiquenet

2
bu benim için işe yaramıyor. p.StandardOutput.ReadToEnd();asla çıkmaz
Louis Rhys

2

Düşünülmesi gereken iki nokta var.

1) Bu kaynak kodu benim için çalıştı:

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

Ben çalışma dizini komut dosyası dizinine ayarlayın, böylece komut dosyası içindeki alt komut dosyaları da çalışır.

Örneğin, Execute("usr/pwd@service", "c:\myscripts", "script.sql")

2) SQL betiğinizi ifadeyle sonlandırmanız gerekir EXIT;


1

EntityFramework kullanarak, böyle bir çözümle gidebilirsiniz. Bu kodu e2e testlerini başlatmak için kullanıyorum. Sql enjeksiyon saldırılarını önlemek için, bu komut dosyasını kullanıcı girişine göre oluşturmamaya dikkat edin veya bunun için komut parametreleri kullanmayın (parametreleri kabul eden ExecuteSqlCommand'ın aşırı yüklenmesine bakın).

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

Bunu yapmanın kesin ve geçerli bir yolunu bulamadım. Bir gün sonra, farklı kaynaklardan elde edilen ve işi yapmaya çalıştığım bu karışık kodla geldim.

Ancak ExecuteNonQuery: CommandText property has not been Initializedkomut dosyasını başarıyla çalıştırsa bile hala bir istisna oluşturuyor - benim durumumda, veritabanını başarıyla oluşturur ve ilk başlangıçta veri ekler.

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
        {

        }

    }

}

Bunu yapmanın kesin ve geçerli bir yolunu bulamadım. Bir gün sonra, farklı kaynaklardan elde edilen ve işi yapmaya çalıştığım bu karışık kodla geldim. bu yüzden hepsini birleştirdim ve sonucu elde ettim. Ancak yine de "ExecuteNonQuery: CommandText özelliği Başlatılmadı" özel durumu oluşturuyor. Rağmen komut dosyası başarıyla çalıştırılır (Benim durumumda, başarıyla veritabanı oluşturmak ve ilk başlangıçta veri eklemek).
Muhammed Salman
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.