Điều dễ dàng nhất có thể là chỉ cần in số bạn nhận lại cho ExecuteNonQuery
:
int rowsAffected = server.ConnectionContext.ExecuteNonQuery(/* ... */);
if (rowsAffected != -1)
{
Console.WriteLine("{0} rows affected.", rowsAffected);
}
Điều này sẽ hoạt động, nhưng sẽ không tôn trọng SET NOCOUNT
cài đặt của phiên / phạm vi hiện tại.
Nếu không, bạn sẽ làm như cách bạn làm với ADO.NET "đơn giản". Không sử dụng ServerConnection.ExecuteNonQuery()
nhưng hãy tạo một SqlCommand
đối tượng bằng cách truy cập SqlConnection
bên dưới vật. Trên đó, đăng ký StatementCompleted
sự kiện.
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();
}
Sử dụng StatementCompleted
(thay vào đó, giả sử in thủ công giá trị ExecuteNonQuery()
trả về) có lợi ích là nó hoạt động chính xác như SSMS hoặc SQLCMD.EXE sẽ:
- Đối với các lệnh không có ROWCOUNT, lệnh đó sẽ hoàn toàn không được gọi (ví dụ:ĐI, SỬ DỤNG).
- Nếu
SET NOCOUNT ON
đã được thiết lập, nó sẽ hoàn toàn không được gọi. - Nếu
SET NOCOUNT OFF
đã được thiết lập, nó sẽ được gọi cho mọi câu lệnh trong một lô.
(Thanh bên:có vẻ như StatementCompleted
chính xác là những gì giao thức TDS nói về khi DONE_IN_PROC
sự kiện được đề cập; xem Nhận xét
của lệnh SET NOCOUNT trên MSDN.)
Cá nhân tôi đã sử dụng phương pháp này thành công trong "bản sao" SQLCMD.EXE của riêng tôi.
CẬP NHẬT :Cần lưu ý rằng cách tiếp cận này (tất nhiên) yêu cầu bạn phải tách thủ công tập lệnh / câu lệnh đầu vào tại GO
dấu phân tách, vì bạn đang quay lại sử dụng SqlCommand.Execute*()
mà không thể xử lý nhiều lô cùng một lúc. Đối với điều này, có nhiều lựa chọn:
- Tách thủ công đầu vào trên các dòng bắt đầu bằng
GO
(báo trước:GO
có thể được gọi làGO 5
, ví dụ:để thực hiện lô trước đó 5 lần). - Sử dụng ManagedBatchParser class / library để giúp bạn chia dữ liệu đầu vào thành các lô duy nhất, đặc biệt là triển khai ICommandExecutor.ProcessBatch với đoạn mã ở trên (hoặc cái gì đó tương tự như nó).
Tôi chọn tùy chọn sau, cách này khá hiệu quả, vì nó không được ghi chép đầy đủ và rất hiếm ví dụ (hãy google một chút, bạn sẽ tìm thấy một số thứ hoặc sử dụng phản xạ để xem cách SMO-Assemblies sử dụng lớp đó) .
Lợi ích (và có thể là gánh nặng) của việc sử dụng ManagedBatchParser
nghĩa là nó cũng sẽ phân tích cú pháp tất cả các cấu trúc khác của tập lệnh T-SQL (dành cho SQLCMD.EXE
) cho bạn. Bao gồm::setvar
, :connect
, :quit
, v.v. Bạn không phải triển khai ICommandExecutor
tương ứng tất nhiên, nếu các tập lệnh của bạn không sử dụng chúng. Nhưng xin lưu ý với bạn rằng bạn sẽ không thể thực thi các tập lệnh "tùy ý".
Vâng, đã làm điều đó đặt bạn. Từ "câu hỏi đơn giản" về cách in "... hàng bị ảnh hưởng" đến thực tế là không hề nhỏ để thực hiện một cách mạnh mẽ và chung chung (với yêu cầu công việc nền). YMMV, chúc may mắn.
Cập nhật về Sử dụng ManagedBatchParser
Dường như không có tài liệu hoặc ví dụ nào tốt về cách triển khai IBatchSource
, đây là những gì tôi đã đi cùng.
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;
}
}
Và đây là cách bạn sẽ sử dụng nó:
var source = new StatementBatchSource("SELECT GETUTCDATE()");
source.Populate();
var parser = new Parser();
parser.SetBatchSource(source);
/* other parser.Set*() calls */
parser.Parse();
Lưu ý rằng cả hai cách triển khai, hoặc cho các câu lệnh trực tiếp (StatementBatchSource
) hoặc cho một tệp (FileBatchSource
) có vấn đề là họ đọc toàn bộ văn bản cùng lúc trong bộ nhớ. Tôi đã gặp một trường hợp mà điều đó đã nổ tung, có một tập lệnh (!) Khổng lồ với các gazillions của INSERT
được tạo các câu lệnh. Mặc dù tôi không nghĩ đó là một vấn đề thực tế, SQLCMD.EXE
có thể xử lý nó. Nhưng đối với cuộc đời của tôi, tôi không thể tìm ra cách chính xác, bạn sẽ cần phải tạo các khối được trả về cho IBatchParser.GetContent()
để theparser vẫn có thể làm việc với chúng (có vẻ như chúng cần phải là những câu lệnh hoàn chỉnh, điều này sẽ đánh bại mục đích của phân tích cú pháp ngay từ đầu ...).