如何使用非常規參數調用存儲過程?

c# dapper sql-server stored-procedures

我正在嘗試將Red Gate的SQLBackup Pro軟件集成到我用C#編寫的內部備份軟件中。這樣做的自然方式是通過擴展存儲過程 。問題在於它以我以前從未見過的格式調用:

master..sqlbackup '-SQL "BACKUP DATABASE pubs TO DISK = [C:\Backups\pubs.sqb]"'

通過SSMS運行時,這很好用。遇到麻煩的地方是嘗試從C#調用它(使用.NET 4和Dapper Dot Net )。

我的第一次嘗試不起作用,因為它將整個cmd字符串解釋為存儲過程的名稱,並拋出錯誤“找不到存儲過程''”:

var cmd = "master..sqlbackup '-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'";
connection.Execute(cmd, commandType: CommandType.StoredProcedure, commandTimeout: 0);

我的第二次嘗試立即返回並顯示(到C#)成功,但實際上沒有進行備份(這也很難進行參數化):

var cmd = "master..sqlbackup";
var p = new DynamicParameters();
p.Add("", "'-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'");
connection.Execute(cmd, p, commandType: CommandType.StoredProcedure, commandTimeout: 0);

我的第三次嘗試似乎也成功了,但實際上沒有進行備份:

var cmd = "master..sqlbackup '-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'";
connection.Execute(cmd, commandTimeout: 0);

我錯過了什麼?

更新1:

我忽略了Red Gate文檔,該文檔說存儲的proc實際上不會引發SQL錯誤,它只是在輸出表中返回錯誤。油滑。這可能解釋了為什麼我在上面的第二次和第三次測試中得到了無聲的失敗:一些潛在的問題,他們沒有收集輸出來說明原因。

這就是我現在的位置:

var cmd = "master..sqlbackup";
var p = new DynamicParameters();
p.Add("", "'-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'");
p.Add("@exitcode", DbType.Int32, direction: ParameterDirection.Output);
p.Add("@sqlerrorcode", DbType.Int32, direction: ParameterDirection.Output);
connection.Execute(cmd, p, commandType: CommandType.StoredProcedure, commandTimeout: 0);

當我運行它並檢查那些輸出參數時,我得到退出代碼870:

沒有命令傳遞給SQL備份。

該命令為空。

所以它沒有看到空命名的參數。

更新2:

在跟踪中捕獲上述內容表明空參數字符串最終被@Parameter1=替換,這解釋了存儲過程無法看到它的原因。

一般承認的答案

這很糟糕,根本不是我想要的,但這就是我現在所做的工作:

var cmd = String.Format(@"
DECLARE @exitcode int; 
DECLARE @sqlerrorcode int;
EXEC master..sqlbackup '-SQL \"BACKUP DATABASE [{0}] TO DISK = [{1}])\"', @exitcode OUTPUT, @sqlerrorcode OUTPUT;
IF (@exitcode >= 500) OR (@sqlerrorcode <> 0)
BEGIN
RAISERROR('SQLBackup failed with exitcode %d and sqlerrorcode %d ', 16, 1, @exitcode, @sqlerrorcode)
END
", myDbName, myBkpPath);

connection.Execute(cmd, commandTimeout: 0);

這會執行備份並實際返回失敗狀態,這會暴露導致失敗部分靜默失敗的潛在問題。

在運行之前,將針對已知數據庫列表檢查myDbName以確保它存在並且myBkpPath由我的代碼生成,因此我不擔心注入。這只是......好吧,看看那個。可怕。


熱門答案

你的第一次嘗試看起來幾乎是對我注意到你沒能逃脫反斜杠。對於這種情況,使用@前綴來禁用字符串的轉義通常更容易。此外,您希望使用exec前置並使其成為CommandType.Text

編輯:修復我自己的逃避錯誤

var cmd = @"exec 'master..sqlbackup -SQL ""BACKUP DATABASE pubs TO DISK = [C:\Backups\pubs.sqb]""'";
connection.Execute(cmd, commandType: CommandType.Text, commandTimeout: 0);


許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因