【问题标题】:SQL CLR issue: CREATE PROCEDURE failed because parameter counts do not matchSQL CLR 问题:CREATE PROCEDURE 失败,因为参数计数不匹配
【发布时间】:2020-10-28 23:36:55
【问题描述】:

我确实检查了this,但问题似乎不是类型不匹配。

我正在尝试使用 CLR 从 blob 数据转换回文件。下面是转换成dll并使用汇编存储的c#代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlTypes;
using System.IO;
using Microsoft.SqlServer.Server;


namespace CLRProcedures
{
   public class WriteFile
    {
        [SqlFunction]
        public static SqlInt32 WriteToFile(SqlBytes binary, SqlString path, SqlBoolean append)
        {
            try
            {
                if (!binary.IsNull && !path.IsNull && !append.IsNull)
                {
                    var dir = Path.GetDirectoryName(path.Value);
                    if (!Directory.Exists(dir))
                        Directory.CreateDirectory(dir);
                    using (var fs = new FileStream(path.Value, append ? FileMode.Append : FileMode.OpenOrCreate))
                    {
                        byte[] byteArr = binary.Value;
                        for (int i = 0; i < byteArr.Length; i++)
                        {
                            fs.WriteByte(byteArr[i]);
                        };
                    }
                    return 1;
                }
                else
                   return 0;
            }
            catch (Exception ex)
            {
                return -2;
            }
        }
    }
}

SQL查询如下:

CREATE ASSEMBLY FileOutput from 'c:\dlls\CLRProcedures.dll' WITH PERMISSION_SET = SAFE 

CREATE PROCEDURE FileOutput
@file varbinary(max),
@filepath nvarchar(4000),
@append bit,
@message int OUTPUT    
AS    
EXTERNAL NAME FileOutput.[CLRProcedures.WriteFile].WriteToFile

这会引发错误:'CREATE PROCEDURE 失败,因为参数计数不匹配。'

我已经从here 重新验证是否存在类型不匹配但没有。我哪里出错了?我试图更改 c# 代码的返回类型,但同样的问题。

谢谢

【问题讨论】:

  • 去掉@message int OUTPUT参数还能用吗?
  • output 与返回值不一样; output 有点像 .NET 中的 ref
  • docs.microsoft.com/en-us/sql/database-engine/dev-guide/… 展示了如果您真的想这样做,如何使用 OUTPUT 参数。
  • 名为“message”的参数应该是什么数据类型?对我来说听起来不像数字。

标签: c# sql-server file tsql sqlclr


【解决方案1】:

需要在WriteToFile函数中添加OUTPUT参数才能返回值

[SqlFunction]
public static void WriteToFile(SqlBytes binary, SqlString path, SqlBoolean append,out SqlInt32 message)
    {
        try
        {
            if (!binary.IsNull && !path.IsNull && !append.IsNull)
            {
                var dir = Path.GetDirectoryName(path.Value);
                if (!Directory.Exists(dir))
                    Directory.CreateDirectory(dir);
                using (var fs = new FileStream(path.Value, append ? FileMode.Append : FileMode.OpenOrCreate))
                {
                    byte[] byteArr = binary.Value;
                    for (int i = 0; i < byteArr.Length; i++)
                    {
                        fs.WriteByte(byteArr[i]);
                    };
                }
                message = 1;
            }
            else
                message = 0;
        }
        catch (Exception ex)
        {
            message = -2;
        }
    }

更多详情:CLR Stored Procedures

【讨论】:

  • 我怀疑这是走错了方向——真正的“修复”是从 SP 声明中删除参数(改用返回值)
  • @MarcGravell 为什么?他可以将缺失的参数添加到方法中并返回消息值
  • @NajiMakhoul 他可以,但该参数用于指示成功/失败状态,这是存储过程返回值的惯用用法
  • 感谢您解释实际情况。由于 Path.GetDirectoryName 引发错误,我仍然无法导出文件'请求类型'System.Security.Permissions.FileIOPermission,mscorlib,Version = 4.0.0.0,Culture = Neutral,PublicKeyToken = b77a5c561934e089'的权限失败'。 Google 搜索建议在创建程序集时将权限模式设置为 UNSAFE。当我尝试这样做时,会引发错误,即无法加载“程序集 'CLRProcedures',因为此版本的 SQL Server 仅支持 SAFE 程序集。”这意味着它的 linux 托管 sql server ??
  • 以前我试图通过“OLE 自动化程序”从 blob 数据创建文件,但由于上级当局没有建议,所以无法启用它。[即使我尝试启用,它说不能在这个版本的sql server上完成]。现在我被告知通过 bcp 尝试,但不知道如何接近 -
【解决方案2】:

您忽略了错误消息的内容:

CREATE PROCEDURE 失败,因为参数计数 不匹配(已添加重点)

问题在于参数“count”,而不是任何特定的参数“type”。您在方法签名中有 3 个输入参数,但在 CREATE PROCEDURE 语句中指定了 4 个参数。

您的选择是:

  1. 按照 Naji 的建议,添加第 4 个参数,在 C# 代码中声明为 out,并将 return 更改为设置 message 变量,这将匹配您定义的 4 个参数。李>
  2. 或者,您可以保持 C# 代码原样并以与常规 T-SQL 存储 proc 返回值相同的方式获取该返回值:
    DECLARE @ReturnValue INT;
    
    EXEC @ReturnValue = dbo.FileOutput 0x1234, N'path', 1;
    
    SELECT @ReturnValue;
    
  3. 或者,您甚至可以通过简单地将 T-SQL CREATE 对象语句更改为:
    CREATE FUNCTION dbo.FileOutput
    (
      @File VARBINARY(MAX),
      @FilePath NVARCHAR(4000),
      @Append BIT
    )
    RETURNS INT
    AS EXTERNAL NAME ...
    
    并使用如下:
    DECLARE @ReturnValue INT;
    
    SET @ReturnValue = dbo.FileOutput(0x1234, N'path', 1);
    
    SELECT @ReturnValue;
    
    (从技术上讲,作为一个返回 INT / Int32 的标量函数,您甚至可以使用与上面选项 2 中所示相同的存储过程语法来执行它)

另外:

  1. 如果你正在创建一个过程,你应该用SqlProcedure而不是SqlFunction来装饰C#WriteToFile方法。
  2. SqlBytes 类型提供了一个Stream 属性,它是一个实际流,它是读取该值的更有效方式,而不是迭代整个二进制值作为byte[]。只需将binary.Stream 复制到文件流即可:-)
  3. 始终指定架构名称(用于架构绑定对象),无论是在CREATE 语句、SELECTEXEC 等中。

有关使用 SQLCLR 的更多信息,请访问:SQLCLR Info

【讨论】:

  • 感谢您解释实际情况。由于 Path.GetDirectoryName 引发错误,我仍然无法导出文件'请求类型'System.Security.Permissions.FileIOPermission,mscorlib,Version = 4.0.0.0,Culture = Neutral,PublicKeyToken = b77a5c561934e089'的权限失败'。 Google 搜索建议在创建程序集时将权限模式设置为 UNSAFE。当我尝试这样做时,会引发错误,即无法加载“程序集 'CLRProcedures',因为此版本的 SQL Server 仅支持 SAFE 程序集。”这意味着它的 linux 托管 sql server ??
  • 以前我试图通过“OLE 自动化程序”从 blob 数据创建文件,但由于上级当局没有建议,所以无法启用它。[即使我尝试启用,它说不能在这个版本的sql server上完成]。现在我被告知通过 bcp 尝试,但不知道如何接近
  • @ShaliniRaj 请使用该附加信息更新主要问题:a)您实际尝试完成的任务(最终目标,而不仅仅是导出文件的中间步骤),b)什么版本/您正在使用的 SQL Server 的平台。谢谢。这将有助于读者更好地了解正在发生的事情,但听起来实际问题已得到解决/回答,您现在正在转向另一个问题。
  • @ShaliniRaj 我可以说,如果您在 Linux 上运行,那么它将无法执行文件系统的工作。无论哪种方式,UNSAFE 都是不好的建议,因为它是不必要的。 EXTERNAL_ACCESS 会处理这个问题。请参阅:Stairway to SQLCLR Level 4: Security (EXTERNAL and UNSAFE Assemblies)SQLCLR vs. SQL Server 2017, Part 2: “CLR strict security” – Solution 1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-22
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多