首页 文章

参数如何从SQL Server传递到基于CLR的存储过程?

提问于
浏览
2

使用此示例:

http://www.sommarskog.se/dynsearch-2008/search_orders_cs.cs

....我构建并成功地将基于CLR的存储过程部署到SQL Server . 在SSMS中查看生成的存储过程定义时(请参阅下面的代码),我注意到C#定义中定义的参数确实是SP定义的一部分,但是当您查看对存储过程的实际调用时,没有参考参数,所以我很好奇参数值是如何实际传递的?

(我特别要问的是,在c#代码中,我想知道我是否可以将 Query 变量设置为我想要的任何现有存储过程,然后在一个大的varchar参数中传递 all 相关参数作为分隔的KeyValue对,然后将它们拆分并在循环中将它们添加为Command.Parameters . 基本上,我正在尝试构建一个可以执行任何其他存储过程的通用存储过程,而不会对参数的变化计数或数据类型进行任何限制,所有这些将在运行时读取 . 基本上,从C#调用SQL Server时具有相同的灵活性,除了在SQL Server中实现 . )

产生的存储过程(在SSMS中查看):

CREATE PROCEDURE [dbo].[search_orders_cs]
    @Orderid [int],
    @Fromdate [datetime],
    @Todate [datetime],
    @Minprice [money],
    @Maxprice [money],
    @Custid [nvarchar](4000),
    @Custname [nvarchar](4000),
    @City [nvarchar](4000),
    @Region [nvarchar](4000),
    @Country [nvarchar](4000),
    @Prodid [int],
    @Prodname [nvarchar](4000),
    @Debug [bit]
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [SqlServerProject1].[StoredProcedures].[search_orders_cs]
GO


EXEC sys.sp_addextendedproperty @name=N'SqlAssemblyFile', @value=N'twg_clr_based_sp.cs' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'search_orders_cs'
GO

EXEC sys.sp_addextendedproperty @name=N'SqlAssemblyFileLine', @value=N'23' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'search_orders_cs'
GO

C#来源:

using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void search_orders_cs(
           SqlInt32 Orderid,
           SqlDateTime Fromdate,
           SqlDateTime Todate,
           SqlMoney Minprice,
           SqlMoney Maxprice,
           SqlString Custid,
           SqlString Custname,
           SqlString City,
           SqlString Region,
           SqlString Country,
           SqlInt32 Prodid,
           SqlString Prodname,
           SqlBoolean Debug)
    {
        string Query;
        SqlCommand Command = new SqlCommand();

        Query = @"SELECT o.OrderID, o.OrderDate, od.UnitPrice, od.Quantity,
                     c.CustomerID, c.CompanyName, c.Address, c.City,
                     c.Region, c.PostalCode, c.Country, c.Phone,
                     p.ProductID, p.ProductName, p.UnitsInStock,
                     p.UnitsOnOrder
              FROM   dbo.Orders o
              JOIN   dbo.[Order Details] od ON o.OrderID = od.OrderID
              JOIN   dbo.Customers c ON o.CustomerID = c.CustomerID
              JOIN   dbo.Products p ON p.ProductID = od.ProductID
              WHERE  1 = 1 ";

        if (!Orderid.IsNull)
        {
            Query += " AND o.OrderID = @orderid " +
                     " AND od.OrderID = @orderid";
            Command.Parameters.Add("@orderid", SqlDbType.Int);
            Command.Parameters["@orderid"].Value = Orderid;
            Command.Parameters["@orderid"].Direction = ParameterDirection.Input;
        }

        if (!Fromdate.IsNull)
        {
            Query += " AND o.OrderDate >= @fromdate";
            Command.Parameters.Add("@fromdate", SqlDbType.DateTime);
            Command.Parameters["@fromdate"].Value = Fromdate;
            Command.Parameters["@fromdate"].Direction = ParameterDirection.Input;
        }

        if (!Todate.IsNull)
        {
            Query += " AND o.OrderDate <= @todate";
            Command.Parameters.Add("@todate", SqlDbType.DateTime);
            Command.Parameters["@todate"].Value = Todate;
            Command.Parameters["@todate"].Direction = ParameterDirection.Input;
        }

        if (!Minprice.IsNull)
        {
            Query += " AND od.UnitPrice >= @minprice";
            Command.Parameters.Add("@minprice", SqlDbType.Money);
            Command.Parameters["@minprice"].Value = Minprice;
            Command.Parameters["@minprice"].Direction = ParameterDirection.Input;
        }

        if (!Maxprice.IsNull)
        {
            Query += " AND od.UnitPrice <= @maxprice";
            Command.Parameters.Add("@maxprice", SqlDbType.Money);
            Command.Parameters["@maxprice"].Value = Maxprice;
            Command.Parameters["@maxprice"].Direction = ParameterDirection.Input;
        }

        if (!Custid.IsNull)
        {
            Query += " AND o.CustomerID = @custid" +
                     " AND c.CustomerID = @custid";
            Command.Parameters.Add("@custid", SqlDbType.NChar, 5);
            Command.Parameters["@custid"].Value = Custid;
            Command.Parameters["@custid"].Direction = ParameterDirection.Input;
        }

        if (!Custname.IsNull)
        {
            Query += " AND c.CompanyName LIKE @custname + '%'";
            Command.Parameters.Add("@custname", SqlDbType.NVarChar, 40);
            Command.Parameters["@custname"].Value = Custname;
            Command.Parameters["@custname"].Direction = ParameterDirection.Input;
        }

        if (!City.IsNull)
        {
            Query += " AND c.City = @city";
            Command.Parameters.Add("@city", SqlDbType.NVarChar, 15);
            Command.Parameters["@city"].Value = City;
            Command.Parameters["@city"].Direction = ParameterDirection.Input;
        }

        if (!Region.IsNull)
        {
            Query += " AND c.Region = @region";
            Command.Parameters.Add("@region", SqlDbType.NVarChar, 15);
            Command.Parameters["@region"].Value = Region;
            Command.Parameters["@region"].Direction = ParameterDirection.Input;
        }

        if (!Country.IsNull)
        {
            Query += " AND c.Country = @country";
            Command.Parameters.Add("@country", SqlDbType.NVarChar, 15);
            Command.Parameters["@country"].Value = Country;
            Command.Parameters["@country"].Direction = ParameterDirection.Input;
        }

        if (!Prodid.IsNull)
        {
            Query += " AND od.ProductID = @prodid" +
                     " AND p.ProductID = @prodid";
            Command.Parameters.Add("@prodid", SqlDbType.Int);
            Command.Parameters["@prodid"].Value = Prodid;
            Command.Parameters["@prodid"].Direction = ParameterDirection.Input;
        }

        if (!Prodname.IsNull)
        {
            Query += " AND p.ProductName LIKE @prodname + '%'";
            Command.Parameters.Add("@prodname", SqlDbType.NVarChar, 40);
            Command.Parameters["@prodname"].Value = Prodname;
            Command.Parameters["@prodname"].Direction = ParameterDirection.Input;
        }

        Query += " ORDER BY o.OrderID";

        using (SqlConnection Connection = new SqlConnection("context connection=true"))
        {
            Connection.Open();

            if (Debug)
            {
                SqlContext.Pipe.Send(Query);
            }

            Command.CommandType = CommandType.Text;
            Command.CommandText = Query;
            Command.Connection = Connection;
            SqlContext.Pipe.ExecuteAndSend(Command);
        }
    }
};

2 回答

  • 0

    没有参考参数,所以我很好奇参数值是如何实际传递的?

    它是在SQL Server中看到并与via T交互的T-SQL包装器对象(即存储过程,函数,用户定义类型,用户定义聚合或触发器)之间的相同签名(仅限数据类型和位置) -SQL(例如问题中的 CREATE PROCEDURE 示例)和通过该T-SQL包装器对象的 EXTERNAL NAME 子句引用的.NET方法 .

    参数名称在T-SQL包装器对象参数和.NET方法参数之间不匹配 . 您可以通过重新排列问题中显示的存储过程中的参数名称来验证这一点,或者甚至只将它们全部重命名为 @a@b ,'@c'等 . 然后将 CREATE 更改为 ALTER ,运行它,然后运行存储过程再次使用相同的输入,它仍然可以像参数名称更改之前一样工作 .


    在相关的说明中,SQLCLR的特定用法是毫无意义的 . 它在纯T-SQL中做同样的事情没有好处(即在3个NVARCHAR(MAX)变量中动态创建查询,参数列表和参数值列表,然后在第二个动态创建的查询中组合它们)只是 EXEC sp_executesql 调用 . 现在,如果那个动态搜索过程将是一个TVF,那将是对SQLCLR的有效使用,因为它会做一些在T-SQL中无法完成的事情:函数中的动态SQL!

  • 2

    下面是一个可用于调用DLL中的方法的示例 .

    步骤1.在Visual Studio中创建库类

    using System;
    using System.Data.SqlTypes;
    using Microsoft.SqlServer.Server;
    
    namespace MyNamespace
    {
        public static class MyClass
        {
            [SqlProcedure]
            public static void MyMethod(SqlString strInParam, out SqlString strOutParam)
            {
                strOutParam = $"Hi '{strInParam}', The date time is: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
            }
        }
    }
    

    步骤2.创建装配

    CREATE ASSEMBLY [AssemblyName]
        AUTHORIZATION dbo
        FROM 'DLL_Path'
        WITH PERMISSION_SET = SAFE
    GO
    

    步骤3.创建一个过程

    -- DROP PROCEDURE MyProcedure
    CREATE PROCEDURE MyProcedure(@strInParam nvarchar(1000), @strOutParam nvarchar(1000) OUTPUT)
        AS EXTERNAL NAME [AssemblyName].[MyNamespace.MyClass].[MyMethod]
    GO
    

    步骤4.启用CLR

    EXEC sp_configure 'clr enabled', 1
    GO
    RECONFIGURE
    GO
    EXEC sp_configure 'clr enabled'
    GO
    

    步骤5.执行存储过程将导致对写入的方法进行调用

    DECLARE @res NVARCHAR(1000);
    EXEC dbo.MyProcedure @strInParam = 'Siya', @strOutParam = @res OUTPUT;
    
    SELECT @res
    

    结果是:嗨'Siya',日期时间是:2018/05/19 14:17:47

相关问题