Como executar um arquivo de script .SQL usando c #

Tenho certeza de que essa pergunta já foi respondida, mas não consegui encontrar uma resposta usando a ferramenta de pesquisa.

Usando c # eu gostaria de executar um arquivo .sql. O arquivo sql contém várias instruções sql, algumas das quais são quebradas em várias linhas. Eu tentei ler no arquivo e tentei executar o arquivo usando o ODP.NET … no entanto, eu não acho que o ExecuteNonQuery foi realmente projetado para fazer isso.

Então eu tentei usar o sqlplus via spawning de um processo … no entanto, a menos que eu gerasse o processo com o uso de UseShellExecute definido como verdadeiro, o sqlplus seria interrompido e nunca sairia. Aqui está o código que não funciona.

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 nunca retorna …. A menos que eu defina UseShellExecute como true. Um efeito colateral de UseShellExecute é que você não pode capturar a saída redirecionada.

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

Eu tentei essa solução com o Microsoft.SqlServer.Management, mas ela não funcionou bem com o .NET 4.0, então escrevi outra solução usando o .NET libs framework apenas.

  string script = File.ReadAllText(@"E:\someSqlScript.sql"); // split script on GO command IEnumerable commandStrings = Regex.Split(script, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase); Connection.Open(); foreach (string commandString in commandStrings) { if (commandString.Trim() != "") { using(var command = new SqlCommand(commandString, Connection)) { command.ExecuteNonQuery(); } } } Connection.Close(); 

Coloque o comando para executar o script sql em um arquivo de lote e execute o código abaixo

 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 ) 

no arquivo em lote escrever algo como isto (amostra para o servidor sql)

 osql -E -i %1 

Isso funciona no Framework 4.0 ou superior. Suporta “GO”. Também mostre a mensagem de erro, linha e comando 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 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; } } 

Adicionado melhorias adicionais para resposta 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); } } } } 

Além disso, tive que adicionar as seguintes referências ao meu projeto:

  • 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

Eu não tenho idéia se essas são as DLLs certas a serem usadas, pois existem várias pastas em C: \ Arquivos de Programas \ Microsoft SQL Server, mas na minha aplicação essas duas funcionam.

Eu consegui descobrir a resposta lendo o manual 🙂

Este extrato do MSDN

O exemplo de código evita uma condição de deadlock chamando p.StandardOutput.ReadToEnd antes de p.WaitForExit. Uma condição de deadlock pode resultar se o processo pai chama p.WaitForExit antes de p.StandardOutput.ReadToEnd e o processo filho grava texto suficiente para preencher o stream redirecionado. O processo pai aguardaria indefinidamente que o processo filho fosse encerrado. O processo filho esperaria indefinidamente que o pai lesse todo o stream StandardOutput.

Há um problema semelhante quando você lê todo o texto da saída padrão e dos streams de erro padrão. Por exemplo, o seguinte código C # executa uma operação de leitura nos dois streams.

Transforma o código nisso;

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

Que agora sai corretamente.

Isso funciona para mim:

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

Há dois pontos a considerar.

1) Este código fonte funcionou para mim:

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

Eu configurei o diretório de trabalho para o diretório de script, para que os sub-scripts dentro do script também funcionem.

Chame-o, por exemplo, como Execute("usr/pwd@service", "c:\myscripts", "script.sql")

2) Você tem que finalizar seu script SQL com a instrução EXIT;

Usando EntityFramework, você pode ir com uma solução como esta. Eu uso esse código para inicializar os testes e2e. De evitar ataques de injeção sql, certifique-se de não gerar esse script com base na input do usuário ou use os parâmetros de comando para isso (consulte a sobrecarga do ExecuteSqlCommand que aceita parâmetros).

 public static void ExecuteSqlScript(string sqlScript) { using (MyEntities dataModel = new MyEntities()) { // split script on GO commands IEnumerable 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); } } } }