【问题标题】:How do I call a stored procedure with unconventional parameters?如何调用具有非常规参数的存储过程?
【发布时间】:2012-02-09 19:34:25
【问题描述】:

我正在尝试将 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=,这解释了为什么存储过程看不到它。

【问题讨论】:

  • 在sqlbackup上做一个sp_help,我确定param有名字
  • sp_help sqlbackup 仅包括名称、所有者、类型和 Created_datetime 列。即使类型是“扩展存储过程”,也没有 param_order(或其他任何名称)列...
  • 我用 Red Gate 打开了一个案例,以了解该参数是否有名称。我们拭目以待。
  • 根据Red Gate的说法,这个参数没有名字。它只使用第一个位置的任何内容。这看起来很傻......

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


【解决方案1】:

您的第一次尝试看起来几乎是正确的。我注意到您未能逃脱反斜杠。对于这种事情,使用 @ 前缀来禁用对字符串的转义通常更容易。另外,您想在前面加上 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);

【讨论】:

  • 对不起,反斜杠实际上被转义了,但问题中没有出现。我选择转义它们而不是使用@"" 语法,因为引号已经是最令人困惑的部分,并且路径(一旦弄清楚)将通过参数或(上帝禁止)字符串格式插入。
  • 您是否尝试添加我也提到的 exec?
  • 是的。与 #2 和 #3 相同的结果:不做任何工作就立即返回,但似乎没有失败。
  • @sh-beta:更重要的是,您是否将您的从 CommandType.StoredProcedure 更改为 CommandType.Text?这是主要问题。
  • @ChrisLively 是的。请参阅第三次尝试。
【解决方案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 是由我的代码生成的,所以我不担心注入。只是……好吧,看看那个。丑陋的。

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 我确信这会奏效,但这不是一个非常可扩展的解决方案,因为必须在我的每台 SQL 服务器上创建和管理相关的存储过程。
    • 不是每个服务器都有扩展存储过程吗? “托管”是什么意思?
    • 扩展存储过程是带有网络感知安装程序的软件包的一部分,可让您轻松地将其推送到网络并通过网络进行升级。我并不是说这对于存储过程是不可能的,而是我宁愿让我的代码与已经存在的东西一起工作。
    【解决方案4】:

    您在测试中遇到了几个问题。

    在第一个中设置CommandType.StoredProcedure。您应该将其设置为CommandType.Text,这样它就足够聪明,只需将该字符串传递给执行即可。

    在随后的示例中,您实际上并未给参数命名。去看看他们的 SqlBackup 过程,看看参数名称是什么。然后使用它。否则,什么都不会分配给它。

    【讨论】:

    • 我的问题中解释了这两个问题。第一个:“我的第一次尝试不起作用,因为它将整个 cmd 字符串解释为存储过程的名称并抛出错误找不到存储过程''”。我的第三次尝试正是这样,但备份无声无息地失败了。对于第二个问题,请参阅我的 cmets。该参数没有记录的名称。
    • @sh-beta:您可以检查 proc 以确定参数名称。无需文档。甚至加密的过程也会告诉你参数的名称是什么。在管理工作室中,您可以展开存储过程定义并查看参数列表。或者你可以右击proc并编写脚本,这样你就可以得到参数的名称。
    • 这个没有提供任何有用的东西。整个脚本定义为“EXEC dbo.sp_addextendedproc N'sqlbackup', ''”
    猜你喜欢
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 2018-08-08
    • 2014-09-05
    相关资源
    最近更新 更多