この質問はすでに回答されているはずですが、検索ツールを使用して回答を見つけることができませんでした。
C#を使用して、.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の副作用は、リダイレクトされた出力をキャプチャできないことです。
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);
}
}
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 (commandString.Trim() != "")
{
using(var command = new SqlCommand(commandString, Connection))
{
command.ExecuteNonQuery();
}
}
}
Connection.Close();
これは、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;
}
}
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
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
C:\ Program Files\Microsoft SQL Serverにはいくつかのフォルダーがありますが、私のアプリケーションではこれらの2つが機能するため、これらが適切なdll:sであるかどうかはわかりません。
私はマニュアルを読んで答えを解決することができました:)
MSDNからのこの抜粋
このコード例では、p.WaitForExitの前にp.StandardOutput.ReadToEndを呼び出すことにより、デッドロック状態を回避しています。親プロセスがp.StandardOutput.ReadToEndの前にp.WaitForExitを呼び出し、子プロセスがリダイレクトされたストリームを満たすのに十分なテキストを書き込む場合、デッドロック状態が発生する可能性があります。親プロセスは、子プロセスが終了するまで無期限に待機します。子プロセスは、親が完全な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;
}
現在は正しく終了します。
これは私のために働く:
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();
}
}
考慮すべき点が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)ステートメントEXIT;
でSQLスクリプトを完成させる必要があります
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);
}
}
}
}