【问题标题】:How to execute an .SQL script file using c#如何使用 C# 执行 .SQL 脚本文件
【发布时间】:2010-10-13 14:25:08
【问题描述】:

我确定这个问题已经得到解答,但是我无法使用搜索工具找到答案。

使用 c# 我想运行一个 .sql 文件。 sql 文件包含多条 sql 语句,其中一些语句被分成多行。我尝试读取文件并尝试使用 ODP.NET 执行文件……但是我不认为 ExecuteNonQuery 真的是为此而设计的。

所以我尝试通过生成进程来使用 sqlplus ... 但是,除非我使用设置为 true 的 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 设置为 true。 UseShellExecute 的一个副作用是您无法捕获重定向的输出。

【问题讨论】:

  • Rich 先生您好,您的问题是关于 Oracle 的,您接受了针对 sql server 的解决方案?您将数据库更改为 sql server 吗?

标签: c# sql oracle scripting


【解决方案1】:
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);
    }
}

【讨论】:

  • 太棒了!这个解决方案对我很有效,因为我能够删除和重新创建数据库,并添加表(通过引用的 SQL 脚本文件)。
  • 此方法不允许在您的脚本中使用“GO”命令,当您从 SQL Management Studio 或 osql 命令运行脚本时,这是允许的。 msdn.microsoft.com/en-us/library/ms188037.aspx
  • Rn222:我认为您混淆了 ExecuteNonQuery 方法,SqlCommand.ExecuteNonQuery 不允许使用“GO”命令,但是 Server.ConnectionContext.ExecuteNonQuery 肯定可以(我现在正在使用它)。
  • 请注意,您需要添加对项目、Microsoft.SqlServer.ConnectionInfo、Microsoft.SqlServer.Management.Sdk 和 Microsoft.SqlServer.Smo 的引用才能使此答案起作用。
  • 对我来说,当使用 .net 4.0/4.5 时,引用 110\SDK\Assemblies 时它不起作用我找到的解决方案是将 app.Config 更改为 <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> </startup>
【解决方案2】:

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

【讨论】:

  • 没错。此解决方案在使用完文件后甚至不会关闭文件。这可能很关键。
  • 使用“RegexOptions.Multiline | RegexOptions.IgnoreCase”来匹配“Go”或“go”的情况。
  • 我认为 RegexOptions.CultureInvariant 标志也应该使用。
  • 这不是 100% 工作:'GO' 可以接受数字参数。
  • 如果GO后面有注释,它会尝试作为命令执行。
【解决方案3】:

这适用于 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;
                            }
                        }
                    }
                }
            }
            return true;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return false;
        }
    }

【讨论】:

  • 不错的代码,一件非常小的事情是不需要connection.Close(),连接将被您包裹的using关闭。
【解决方案4】:

将执行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

【讨论】:

    【解决方案5】:

    这对我有用:

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

    【讨论】:

      【解决方案6】:

      添加了对 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);
                  }
              }
          }
      }
      

      另外,我必须在我的项目中添加以下引用:

      • 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 中对我有用。我不需要 surajit 提到的其他 dll。但是,我必须为 Microsoft.SqlServer.ConnectionInfo 和 Microsoft.SqlServer.Smo 使用 13.0.0.0 版本,因为 13.100.0.0 在实例化 ServerConnection 时会引发异常。
      【解决方案7】:

      我通过阅读手册找到了答案:)

      此摘自 MSDN

      代码示例避免了死锁 通过调用条件 p.StandardOutput.ReadToEnd 之前 p.WaitForExit。死锁条件 如果父进程调用可能会导致 p.WaitForExit before p.StandardOutput.ReadToEnd 和 子进程写入足够的文本到 填充重定向的流。父母 进程将无限期等待 子进程退出。孩子 进程将无限期等待 父母从全文阅读 标准输出流。

      阅读时也有类似的问题 标准输出中的所有文本 和标准错误流。为了 例如,以下 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;
      }
      

      现在正确退出。

      【讨论】:

      • 关于sqlplus的提示:如果您想知道脚本执行是否成功,您可以在脚本开头添加WHENEVER SQLERROR EXIT SQL.SQLCODE。这样sqlplus进程就会返回sql错误号作为返回码。
      • 任何完整的完整源代码示例?什么是 in_database, s ??
      • 这对我不起作用。 p.StandardOutput.ReadToEnd(); 永不退出
      【解决方案8】:

      有两点需要考虑。

      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) 您必须使用语句 EXIT; 完成您的 SQL 脚本

      【讨论】:

        【解决方案9】:

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

        【讨论】:

          【解决方案10】:

          我找不到任何准确有效的方法来做到这一点。所以一整天后,我带着这个从不同来源获得的混合代码,并试图完成工作。

          但即使它成功运行脚本文件,它仍然会生成异常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 属性尚未初始化”。虽然它成功运行了脚本文件(在我的例子中,成功创建数据库并在第一次启动时插入数据)。
          猜你喜欢
          • 1970-01-01
          • 2010-12-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-05
          • 2014-10-28
          • 1970-01-01
          相关资源
          最近更新 更多