Eu tenho um programa c# cliente que está executando procedimentos armazenados via ExectueNonQuery
, incluindo captura de PRINT
saída de erro e com eventos InfoMessage. Funciona bem, mas notei algo estranho.
Quando executo um procedimento armazenado do SSMS, ele exibe o número de linhas para cada instrução SQL individual executada na guia Mensagens (como se viesse das InfoMessages). No entanto, meu programa nunca vê essas mensagens, embora capture todas as outras saídas. Em vez disso, ele apenas retorna as linhas afetadas no resultado da função ExecuteNonQuery, que é a soma de todas as contagens de linhas individuais (o que é meio inútil).
Por exemplo, este procedimento:
use [tempdb]
go
SELECT *
INTO MyCols
FROM sys.columns
go
CREATE PROC foo As
UPDATE MyCols
SET name = name + N''
-- SSMS shows (662 row(s) affected)
UPDATE MyCols
SET name = name + N''
WHERE name like '%x%'
-- SSMS shows (59 row(s) affected)
PRINT 'bar'
-- both SSMS and ExecuteNonQuery get this
-- ExecuteNonQuery returns 721 rows affected
GO
Quando o foo
proc é executado, o SSMS exibe as contagens de linhas de 662 e 59, mas ExecuteNonQuery
retorna apenas o total de 721.
Então, como posso obter as mesmas informações que o SSMS está recebendo?
Só para deixar claro aqui: não estou interessado em como alterar os procedimentos armazenados para adicionar PRINT @@ROWCOUNT
s após cada instrução SQL. Eu sei como fazer isso e não é uma opção na maioria das vezes por vários motivos.
Estou perguntando como fazer o que o SSMS está fazendo aqui. Posso alterar o código do cliente o quanto quiser neste ponto (por enquanto, pelo menos) e gostaria de fazer isso direito.
O
SqlCommand.StatementCompleted
evento será acionado após cada instrução em um lote, e uma das propriedades do evento (bem, praticamente a única propriedade) é o número de linhas afetadas pela instrução que disparou o evento.Algumas notas:
SET NOCOUNT ON;
ou, inversamente , especificouSET NOCOUNT OFF;
.Execute___()
, não durante a execução.StatementCompletedEventArgs.RecordCount
Inclui contagens de linhas de instruçõesSELECT
, enquanto a propriedade SqlDataReader.RecordsAffected relata apenas contagens de linhas de instruções DML (INSERT
,UPDATE
,DELETE
, etc).StatementCompleted
evento não inclui a instrução SQL individual do lote que disparou o evento. No entanto, o manipulador de eventos é enviadosender
como um parâmetro de entrada e este é oSqlCommand
lote de consulta, e você pode ver esse lote convertendosender
paraSqlCommand
e, em seguida, observando aCommandText
propriedade (isso é mostrado no exemplo abaixo).A documentação é muito esparsa sobre isso, então criei um exemplo que mostra esse evento disparando para
ExecuteNonQuery
andExecuteScalar
, bem como para consultas ad hoc e stored procedures (ou seja,SqlCommand.CommandType
ofText
vsStoredProcedure
):RESULTADO:
O resultado executenonquery simplesmente não fará o que você deseja aqui. Mas você ainda pode chegar lá, depende apenas de para que você deseja usar as informações.
Você pode adicionar esta linha após cada inserção "PRINT @@ROWCOUNT" e obter o número de linhas afetadas pela operação anterior como parte da saída (onde você obtém "bar".
Alternativamente, você pode adicionar um parâmetro "OUTPUT" ao seu procedimento armazenado para armazenar os resultados e apenas capturá-los ao executar o executenonquery.
EDITAR:
Consegui modificar a amostra que Jonathan Kehasias montou para incluir a manipulação de eventos statementcompleted. Basta adicionar essas duas linhas.