最も簡単な方法は、ExecuteNonQuery
に対して取得した数値を出力することです。 :
int rowsAffected = server.ConnectionContext.ExecuteNonQuery(/* ... */);
if (rowsAffected != -1)
{
Console.WriteLine("{0} rows affected.", rowsAffected);
}
これは機能するはずですが、SET NOCOUNT
を尊重しません 現在のセッション/スコープの設定。
それ以外の場合は、「プレーンな」ADO.NETの場合と同じように実行します。 ServerConnection.ExecuteNonQuery()
は使用しないでください メソッドですが、SqlCommand
を作成します 基になるSqlConnection
にアクセスしてオブジェクト 物体。その上でStatementCompleted
を購読します イベント。
using (SqlCommand command = server.ConnectionContext.SqlConnectionObject.CreateCommand())
{
// Set other properties for "command", like StatementText, etc.
command.StatementCompleted += (s, e) => {
Console.WriteLine("{0} row(s) affected.", e.RecordCount);
};
command.ExecuteNonQuery();
}
StatementCompleted
の使用 (代わりに、たとえば、ExecuteNonQuery()
の値を手動で出力します 返される)には、SSMSまたはSQLCMD.EXEとまったく同じように機能するという利点があります。
- ROWCOUNTがないコマンドの場合、それはまったく呼び出されません(GO、USEなど)。
-
SET NOCOUNT ON
の場合 設定されているため、まったく呼び出されません。 -
SET NOCOUNT OFF
の場合 が設定されると、バッチ内のすべてのステートメントに対して呼び出されます。
(サイドバー:StatementCompleted
のように見えます DONE_IN_PROC
の場合、TDSプロトコルが正確に説明します。 イベントが言及されています。 備考
を参照してください。 MSDNのSETNOCOUNTコマンドの説明。)
個人的には、このアプローチを使用して、SQLCMD.EXEの独自の「クローン」で成功しました。
更新 :このアプローチでは(もちろん)、GO
で入力スクリプト/ステートメントを手動で分割する必要があることに注意してください。 SqlCommand.Execute*()
の使用に戻ったためです。 一度に複数のバッチを処理することはできません。これには、複数のオプションがあります:
-
GO
で始まる行の入力を手動で分割します (注意:GO
GO 5
のように呼び出すことができます たとえば、前のバッチを5回実行します。 - ManagedBatchParser を使用します 入力を単一のバッチに分割するのに役立つクラス/ライブラリ、特に ICommandExecutor.ProcessBatch 上記のコード(またはそれに似たもの)を使用します。
後者のオプションを選択しますが、これは十分に文書化されておらず、例がまれであることを考えると、かなりの作業でした(少しグーグル、いくつかのものを見つけるか、SMOアセンブリがそのクラスをどのように使用するかを確認するためにリフレクターを使用します) 。
ManagedBatchParser
を使用する利点(そしておそらく負担) つまり、T-SQLスクリプトの他のすべての構成(SQLCMD.EXE
を対象)も解析します。 ) あなたのために。含む::setvar
、:connect
、:quit
、など。それぞれのICommandExecutor
を実装する必要はありません。 もちろん、スクリプトで使用されていない場合はメンバーです。ただし、「任意の」スクリプトを実行できない場合があることに注意してください。
さて、それはあなたを置きましたか。 「影響を受ける行...」を印刷する方法の「単純な質問」から、堅牢で一般的な方法で行うのは簡単ではないという事実まで(必要なバックグラウンド作業を前提として)。 YMMV、頑張ってください。
ManagedBatchParserの使用に関する更新
IBatchSource
の実装方法に関する適切なドキュメントや例はないようです。 、これが私が行ったものです。
internal abstract class BatchSource : IBatchSource
{
private string m_content;
public void Populate()
{
m_content = GetContent();
}
public void Reset()
{
m_content = null;
}
protected abstract string GetContent();
public ParserAction GetMoreData(ref string str)
{
str = null;
if (m_content != null)
{
str = m_content;
m_content = null;
}
return ParserAction.Continue;
}
}
internal class FileBatchSource : BatchSource
{
private readonly string m_fileName;
public FileBatchSource(string fileName)
{
m_fileName = fileName;
}
protected override string GetContent()
{
return File.ReadAllText(m_fileName);
}
}
internal class StatementBatchSource : BatchSource
{
private readonly string m_statement;
public StatementBatchSource(string statement)
{
m_statement = statement;
}
protected override string GetContent()
{
return m_statement;
}
}
そして、これはあなたがそれを使う方法です:
var source = new StatementBatchSource("SELECT GETUTCDATE()");
source.Populate();
var parser = new Parser();
parser.SetBatchSource(source);
/* other parser.Set*() calls */
parser.Parse();
両方の実装は、直接ステートメント(StatementBatchSource
)のいずれかであることに注意してください。 )またはファイルの場合(FileBatchSource
)完全なテキストを一度にメモリに読み込むという問題があります。何億もの生成されたINSERT
を含む巨大な(!)スクリプトがあり、それが爆発したケースが1つありました。 ステートメント。これは実際的な問題ではないと思いますが、SQLCMD.EXE
それを処理することができます。しかし、私の人生では、どれほど正確に理解できなかったので、IBatchParser.GetContent()
に対して返されるチャンクを形成する必要があります。 パーサーが引き続きそれらを処理できるようにするためです(完全なステートメントである必要があるように見えます。これにより、そもそも解析の目的が損なわれる可能性があります...)。