带有 varbinary(max) OUTPUT 的 CLR 存储过程失败,但相同的过程与 T-SQL 一样工作

发布于 2024-12-18 21:00:17 字数 2613 浏览 2 评论 0原文

我有一个存储过程:

CREATE PROCEDURE [dbo].[brbackup]
    @dataID [nvarchar](max),
@backupdata [varbinary](max) OUTPUT
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [SQLBackupRestore].[StoredProcedures].[BackupStuff]
GO

它映射回这个 CLR 存储过程代码:

public class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void BackupStuff(string dataID, out byte [] backupdata)
    {
       [..body omitted..]
    }
 }

这工作得很好,我可以在服务器上的 t-sql 中调用它,如下所示:

declare @backupdata varbinary(max);
exec brbackup "dataIDNumber", @backupdata output;

我得到了预期的输出(@backupdata 中的几兆数据) )。我想做的是通过客户端从 C# 调用它,但这不起作用:

    static void Main(string[] args)
    {
        SqlConnection conn = new SqlConnection("myConnString");
        conn.Open();
        SqlCommand cmd = new SqlCommand("brbackup", conn);
        cmd.CommandTimeout = 0;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@dataID", "dataIDNumber");
        SqlParameter p = new SqlParameter("@backupdata", 
                                           SqlDbType.VarBinary, -1);
        p.Direction = ParameterDirection.Output;
        cmd.Parameters.Add(p);
        cmd.ExecuteNonQuery();
    }

此查询不会给出错误,并且它会运行一段时间(如预期),但是 p.Value 里面除了一个 byte[0] 之外什么都没有。我尝试过将长度设置为 -1 以外的其他值,并将 p.Value 设置为足以容纳结果的 byte[] ,但没有什么乐趣。

然而,奇怪的是,创建一个小的 t-sql 存储过程来包装 CLR 存储过程确实有效:

CREATE PROCEDURE AA_JUST_TESTING
@stuff varbinary(max) output
AS
BEGIN
declare @backupdata varbinary(max);
exec brbackup 'dataIDNumber', @backupdata output;
set @stuff = @backup;
END
GO

然后使用以下 C# 代码调用包装器 AA_JUST_TESTING:

        SqlConnection conn = new SqlConnection("myConnString");
        conn.Open();
        SqlCommand cmd = new SqlCommand("AA_JUST_TESTING", conn);
        cmd.CommandTimeout = 0;
        cmd.CommandType = CommandType.StoredProcedure;
        SqlParameter p = new SqlParameter("@stuff",
                                          SqlDbType.VarBinary, -1);
        p.Direction = ParameterDirection.Output;
        cmd.Parameters.Add(p);
        cmd.ExecuteNonQuery();

效果很好。 p.Value 最终得到一个大小正好且填充了正确数据的 byte []

所以...除了一个调用 CLR 存储过程和另一个调用 T-SQL 存储过程之外,我看不出有什么区别,即使它们都使用 varbinary(max) 输出返回值。我正在寻找:

  • 一个合理的解释,指向文档的指针等...告诉我为什么它会这样工作。

  • 某种解决方法,这样我就不必拥有包装器存储过程,并且可以直接从 C# 调用 CLR SP 来完成我需要的事情。

    某种

I have a stored procedure:

CREATE PROCEDURE [dbo].[brbackup]
    @dataID [nvarchar](max),
@backupdata [varbinary](max) OUTPUT
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [SQLBackupRestore].[StoredProcedures].[BackupStuff]
GO

Which maps back to this CLR Stored procedure code:

public class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void BackupStuff(string dataID, out byte [] backupdata)
    {
       [..body omitted..]
    }
 }

This works just fine, I can call it in t-sql on the server just fine like this:

declare @backupdata varbinary(max);
exec brbackup "dataIDNumber", @backupdata output;

I get the expected output (several megabytes of data in @backupdata). What I'd like to do is call this from C# through a client, but this doesn't work:

    static void Main(string[] args)
    {
        SqlConnection conn = new SqlConnection("myConnString");
        conn.Open();
        SqlCommand cmd = new SqlCommand("brbackup", conn);
        cmd.CommandTimeout = 0;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@dataID", "dataIDNumber");
        SqlParameter p = new SqlParameter("@backupdata", 
                                           SqlDbType.VarBinary, -1);
        p.Direction = ParameterDirection.Output;
        cmd.Parameters.Add(p);
        cmd.ExecuteNonQuery();
    }

This query doesn't give an error, and it runs for a while (as expected), but p.Value has nothing in it but a byte[0]. I've tried variations of setting the length to something other than -1, and setting p.Value to a byte[] large enough to hold the results, but no joy.

However, strangely enough creating a small t-sql stored procedure to wrap around the CLR stored procedure DOES work:

CREATE PROCEDURE AA_JUST_TESTING
@stuff varbinary(max) output
AS
BEGIN
declare @backupdata varbinary(max);
exec brbackup 'dataIDNumber', @backupdata output;
set @stuff = @backup;
END
GO

And then calling the wrapper AA_JUST_TESTING with this C# code:

        SqlConnection conn = new SqlConnection("myConnString");
        conn.Open();
        SqlCommand cmd = new SqlCommand("AA_JUST_TESTING", conn);
        cmd.CommandTimeout = 0;
        cmd.CommandType = CommandType.StoredProcedure;
        SqlParameter p = new SqlParameter("@stuff",
                                          SqlDbType.VarBinary, -1);
        p.Direction = ParameterDirection.Output;
        cmd.Parameters.Add(p);
        cmd.ExecuteNonQuery();

Works just peachy. p.Value winds up with a byte [] of just the right size filled with the correct data.

So... I don't see the difference other than one calls a CLR stored procedure and the other calls a T-SQL stored procedure, even though they're both using a varbinary(max) output to return the value. I'm looking for:

  • A plausible explanation, pointer to documentation, etc... that tells me why this works like it does.

  • Some kind of work-around so I don't have to have the wrapper stored procedure, and can maybe just accomplish what I need calling the CLR SP directly from C#.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

违心° 2024-12-25 21:00:17

奇怪的。尝试将 C# 代码从: 更改

[Microsoft.SqlServer.Server.SqlProcedure]
public static void BackupStuff(string dataID, out byte [] backup)
{
   [..body omitted..]
}

为:

using System.Data.SqlTypes;
...

    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void BackupStuff(string dataID, out SqlBinary backup)
    {
       [..body omitted..]
       byte[] result = ... // get byte array to return
       backup = new SqlBinary(result);
    }

使用 SqlBinary 作为 C# 例程的返回类型对我有用。

编辑:
好吧,使用 SqlBinary 或 byte[] 都适合我。
您的 varbinary(max) 参数是否称为“@backup”或“@backupdata”?看起来您显示的示例代码同时使用了两者。
即尝试将 : 更改

  SqlParameter p = new SqlParameter("@backupdata", 
                                           SqlDbType.VarBinary, -1);

为 :

  SqlParameter p = new SqlParameter("@backup", 
                                           SqlDbType.VarBinary, -1);

Strange. Try changing the C# code from :

[Microsoft.SqlServer.Server.SqlProcedure]
public static void BackupStuff(string dataID, out byte [] backup)
{
   [..body omitted..]
}

To :

using System.Data.SqlTypes;
...

    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void BackupStuff(string dataID, out SqlBinary backup)
    {
       [..body omitted..]
       byte[] result = ... // get byte array to return
       backup = new SqlBinary(result);
    }

Using SqlBinary as the return type of the C# routine works for me.

EDIT :
Well, using either SqlBinary or byte[] both work for me.
Is your varbinary(max) parameter called "@backup" or "@backupdata" ? Looks like the sample code you show uses both.
i.e. try changing :

  SqlParameter p = new SqlParameter("@backupdata", 
                                           SqlDbType.VarBinary, -1);

to :

  SqlParameter p = new SqlParameter("@backup", 
                                           SqlDbType.VarBinary, -1);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文