带输入参数的 SQLCLR 存储过程

SQLCLR stored procedures with input parameter

我对 SQLCLR 存储过程还很陌生。在我的示例中,我有两个存储过程,一个没有参数,一个有输入参数。两者都针对相同的表。

没有参数的那个工作正常,returns 结果中的所有行。但是,即使我没有收到任何错误,带有针对相同表的输入参数的参数也不会返回任何行。输入参数在.NET代码中设置为SqlString,在数据库中设置为NVARCHAR(50).

这是我的 C# 代码的样子:

using System;
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 AirlineSqlStoredProcedure (SqlString strAirline)
    {
        SqlConnection conn = new SqlConnection();
        conn.ConnectionString = "Context Connection=true";

        SqlCommand cmd = new SqlCommand();
        cmd.Connection = conn;
        conn.Open();
        cmd.CommandText = "SELECT  dbo.tblAirline.AirlineName, dbo.tblAircraft.AircraftUnits, dbo.tblAircraft.Manufacturer, dbo.tblAircraft.AircraftModel FROM dbo.tblAircraft INNER JOIN dbo.tblAirline ON dbo.tblAircraft.AirlineID = dbo.tblAirline.AirlineID  WHERE AirlineName = '@strAirline' ORDER BY dbo.tblAircraft.AircraftUnits DESC";
        SqlParameter paramAge = new SqlParameter();
        paramAge.Value = strAirline;
        paramAge.Direction = ParameterDirection.Input;
        paramAge.SqlDbType = SqlDbType.NVarChar;
        paramAge.ParameterName = "@strAirline";

        cmd.Parameters.Add(paramAge);
        SqlDataReader sqldr = cmd.ExecuteReader();
        SqlContext.Pipe.Send(sqldr);

        sqldr.Close();
        conn.Close();
    }

    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void AirlineAircraftStoredProcedure()
    {
        //It returns rows from Roles table    
        SqlConnection conn = new SqlConnection();
        conn.ConnectionString = "Context Connection=true";

        SqlCommand cmd = new SqlCommand();
        cmd.Connection = conn;
        cmd.CommandText = "SELECT  dbo.tblAirline.AirlineName, dbo.tblAircraft.AircraftUnits, dbo.tblAircraft.Manufacturer, dbo.tblAircraft.AircraftModel FROM dbo.tblAircraft INNER JOIN dbo.tblAirline ON dbo.tblAircraft.AirlineID = dbo.tblAirline.AirlineID  ORDER BY dbo.tblAircraft.AircraftUnits DESC";
        conn.Open();

        SqlDataReader sqldr = cmd.ExecuteReader();
        SqlContext.Pipe.Send(sqldr);

        sqldr.Close();
        conn.Close();

    }
}

当我执行存储过程时,我得到空行:

USE [TravelSight]
GO

DECLARE @return_value Int

EXEC    @return_value = [dbo].[AirlineSqlStoredProcedure]
        @strAirline = N'American Airlines'

SELECT  @return_value as 'Return Value'

GO

(0 row(s) affected)

(1 row(s) affected)

此外,对于输入参数,我在字符串前放置了一个 N

当 运行 存储过程 AirlineAircraftStoredProcedure 以相同的表为目标时,我将返回所有行:

USE [TravelSight]
GO

DECLARE @return_value Int

EXEC    @return_value = [dbo].[AirlineAircraftStoredProcedure]

SELECT  @return_value as 'Return Value'

GO


(8 row(s) affected)


(1 row(s) affected)

我做错了什么?

两个(也许是 3 个)问题:

  1. paramAge.Value = strAirline; 应该是:

    paramAge.Value = strAirline.Value;

    注意 .Value 属性.

  2. 的使用
  3. WHERE AirlineName = '@strAirline'(在 cmd.CommandText = "... 内)应该是:

    WHERE AirlineName = @strAirline

    请注意,查询文本中的单引号已被删除。您只对文字使用单引号,而不是参数/变量。

  4. 替换以下5行:

    SqlParameter paramAge = new SqlParameter();
    paramAge.Value = strAirline;
    paramAge.Direction = ParameterDirection.Input;
    paramAge.SqlDbType = SqlDbType.NVarChar;
    paramAge.ParameterName = "@strAirline";
    

    与:

    SqlParameter paramAge = new SqlParameter("@strAirline", SqlDbType.NVarChar, 50);
    paramAge.Direction = ParameterDirection.Input; // optional as it is the default
    paramAge.Value = strAirline.Value;
    

    请注意,"size" 参数是在对 new SqlParameter() 的调用中设置的。 始终 指定最大字符串长度很重要。

解决了技术问题后,还有两个更大的问题需要解决:

  1. 为什么首先要在 SQLCLR 中完成此操作?没有任何特定于 .NET 的事情正在做。仅基于问题中发布的代码,作为常规 T-SQL 存储过程,这会 更好。

  2. 如果它必须保留在 SQLCLR 中,那么您确实需要将一次性对象包装在 using() 构造中,即:SqlConnectionSqlCommand,和 SqlDataReader。例如:

    using (SqlConnection conn = new SqlConnection("Context Connection=true"))
    {
      using (SqlCommand cmd = conn.CreateCommand())
      {
        ...
      }
    }
    

    然后你不需要下面两行:

    sqldr.Close();
    conn.Close();
    

    因为调用它们的每个 Dispose() 方法都会隐式调用它们。