วิธีรันไฟล์สคริปต์. SQL โดยใช้ c #


140

ฉันแน่ใจว่าคำถามนี้ได้รับคำตอบแล้ว แต่ฉันไม่สามารถหาคำตอบได้โดยใช้เครื่องมือค้นหา

ใช้ c # ฉันต้องการเรียกใช้ไฟล์. sql ไฟล์ sql มีหลายคำสั่ง sql ซึ่งบางส่วนจะถูกแบ่งเป็นหลายบรรทัด ฉันลองอ่านไฟล์และลองใช้งานไฟล์โดยใช้ ODP.NET ... แต่ฉันไม่คิดว่า ExecuteNonQuery ได้รับการออกแบบมาให้ทำเช่นนี้จริงๆ

ดังนั้นฉันจึงลองใช้ sqlplus ผ่านกระบวนการวางไข่ ... อย่างไรก็ตามถ้าฉันวางไข่กระบวนการด้วย UseShellExecute ที่ตั้งค่าเป็น 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 เป็นจริง ผลข้างเคียงของ UseShellExecute คือคุณไม่สามารถดักจับเอาต์พุตที่เปลี่ยนทิศทางได้


8
สวัสดีคุณ Rich คำถามของคุณเกี่ยวกับ Oracle และคุณยอมรับโซลูชันที่ใช้กับเซิร์ฟเวอร์ sql หรือไม่ คุณเปลี่ยน DB เป็นเซิร์ฟเวอร์ sql หรือไม่
Akshay J

คำตอบ:


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
ที่ดี! วิธีนี้ใช้ได้ผลกับฉันที่สามารถวางและสร้างฐานข้อมูลและเพิ่มตาราง (ผ่านไฟล์สคริปต์ 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 วิธีแก้ปัญหาที่ฉันพบคือเปลี่ยนแอพกำหนดเป็น<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> </startup>
Abir

107

ฉันลองใช้วิธีแก้ไขปัญหานี้กับ Microsoft.SqlServer.Management แต่ใช้งานไม่ได้กับ NET 4.0 ดังนั้นฉันจึงเขียนโซลูชันอื่นโดยใช้. NET libs framework เท่านั้น

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

เผง โซลูชันนี้จะไม่แม้แต่ปิดไฟล์หลังจากใช้งานเสร็จแล้ว นั่นอาจเป็นสิ่งสำคัญ
Mathias Lykkegaard Lorenzen

1
ใช้ "RegexOptions.Multiline | RegexOptions.IgnoreCase" เพื่อจับคู่กรณี "Go" หรือ "go" ด้วย
Ankush

1
ฉันคิดว่าควรใช้ค่าสถานะ RegexOptions.CultureInvariant เช่นกัน
Dave Andersen

3
สิ่งนี้ไม่ทำงาน 100%: 'GO' อาจยอมรับพารามิเตอร์ตัวเลข
nothrow

16

ใช้งานได้กับ 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;
        }
    }

3
รหัส Nice, สิ่งเล็ก ๆ น้อย ๆ มากอย่างหนึ่งก็คือว่าไม่จำเป็นต้องมีconnection.Close()การเชื่อมต่อจะถูกปิดโดยusingคุณห่อไว้ใน.
มิตร

การทำงานที่ดี. มันทำงาน 'ตรงกล่อง' สำหรับฉัน
Stephen85

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)

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 ที่ถูกต้องหรือไม่เนื่องจากมีหลายโฟลเดอร์ในเซิร์ฟเวอร์ SQL Files \ Microsoft C: \ Program แต่ในแอปพลิเคชันของฉันทั้งสองทำงาน


สิ่งนี้ใช้ได้กับฉันใน. Net 4.7 ฉันไม่ต้องการ DLLs อื่น ๆ ที่ surajit พูดถึง อย่างไรก็ตามฉันต้องใช้รุ่น 13.0.0.0 สำหรับทั้ง Microsoft.SqlServer.ConnectionInfo และ Microsoft.SqlServer.Smo ตามที่ 13.100.0.0 มีข้อยกเว้นเมื่อมีการสร้างอินสแตนซ์ของ ServerConnection
Kevin Fichter

4

ฉันจัดการเพื่อหาคำตอบโดยการอ่านคู่มือ :)

สารสกัดนี้จาก MSDN

ตัวอย่างรหัสหลีกเลี่ยงเงื่อนไขการหยุดชะงักโดยการเรียก p.StandardOutput.ReadToEnd ก่อน p.WaitForExit เงื่อนไขการหยุดชะงักอาจส่งผลหากกระบวนการหลักเรียก p.WaitForExit ก่อน p.StardardOutput.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: หากคุณต้องการทราบว่าการดำเนินการสคริปต์สำเร็จหรือไม่คุณสามารถเพิ่ม SQLERROR EXIT SQL.SQLCODE ได้ที่จุดเริ่มต้นของสคริปต์ วิธีนี้กระบวนการ sqlplus ส่งคืนหมายเลขข้อผิดพลาด sql เป็นรหัสส่งคืน
devdimi

ตัวอย่างซอร์สโค้ดสมบูรณ์แบบใด? in_database คืออะไร?
Kiquenet

2
มันไม่ได้ผลสำหรับฉัน p.StandardOutput.ReadToEnd();ไม่เคยออก
Louis Rhys

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;


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 ยังไม่ได้เริ่มต้น" แม้ว่ามันจะรันไฟล์สคริปต์ได้สำเร็จ (ในกรณีของฉันให้สร้างฐานข้อมูลและใส่ข้อมูลในการเริ่มต้นครั้งแรก)
มูฮัมหมัดซาลมาน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.