最简单的方法可能是打印您返回的ExecuteNonQuery 的号码:
int rowsAffected = server.ConnectionContext.ExecuteNonQuery(/* ... */);
if (rowsAffected != -1)
{
Console.WriteLine("{0} rows affected.", rowsAffected);
}
这应该可以,但不会遵守当前会话/范围的SET NOCOUNT 设置。
否则,您将像使用“普通”ADO.NET 一样进行操作。不要使用ServerConnection.ExecuteNonQuery() 方法,而是通过访问底层SqlConnection 对象来创建SqlCommand 对象。在那订阅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 上SET NOCOUNT 命令的Remarks。)
就我个人而言,我在自己的“克隆”SQLCMD.EXE 中成功地使用了这种方法。
更新:应该注意,这种方法(当然)需要您在GO 分隔符处手动拆分输入脚本/语句,因为您又回到了使用@987654340 @ 不能一次处理多个批次。为此,有多种选择:
我选择了后一个选项,这是相当多的工作,因为它没有很好的文档记录并且示例很少(谷歌一下,你会找到一些东西,或者使用反射器来查看 SMO-Assemblies 是如何使用的那个类)。
使用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 语句。尽管我认为这不是一个实际问题,SQLCMD.EXE 可以处理它。但对于我的一生,我无法弄清楚到底是怎么回事,
您需要形成为IBatchParser.GetContent() 返回的块,以便
解析器仍然可以使用它们(看起来它们需要完整的语句,
这首先会破坏解析的目的......)。