如何使用非常规参数调用存储过程?

时间:2012-02-09 19:34:26

标签: c# sql-server stored-procedures dapper

我正在尝试将Red Gate的SQLBackup Pro软件集成到我用C#编写的内部备份软件中。这样做的自然方式是通过Extended Stored Procedure。问题在于它以我以前从未见过的格式调用:

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文档,该文档说存储过程实际上不会引发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=,这解释了存储过程无法看到它的原因。

4 个答案:

答案 0 :(得分:2)

你的第一次尝试看起来几乎是对的。我注意到你没有逃脱反斜杠。对于这种情况,使用@前缀来禁用字符串的转义通常更容易。此外,您希望在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);

答案 1 :(得分:2)

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

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是由我的代码生成的,因此我不担心注入。它只是......好吧,看看那个。可怕。

答案 2 :(得分:0)

您是否尝试过创建一个调用扩展存储过程的典型存储过程,并从代码中调用它?看起来你只需要处理一些参数。

答案 3 :(得分:0)

你的测试中有几个问题。

在第一个中设置CommandType.StoredProcedure。你应该把它设置为CommandType.Text,这样才能足够智能地将该字符串传递给exec'd。

在后面的示例中,您实际上没有为参数指定名称。去看看他们的SqlBackup过程,看看参数名称是什么。然后使用它。否则,将不会分配任何内容。