程序期望未提供的参数


106

在SQL Server中访问存储过程时出现错误

Server Error in '/' Application.
Procedure or function 'ColumnSeek' expects parameter '@template', which was not supplied. 

当我通过.net与sql的数据连接使用参数调用存储过程时(System.data.SqlClient),即使我提供了该参数,也会发生这种情况。这是我的代码。

SqlConnection sqlConn = new SqlConnection(connPath);
sqlConn.Open();

//METADATA RETRIEVAL
string sqlCommString = "QCApp.dbo.ColumnSeek";
SqlCommand metaDataComm = new SqlCommand(sqlCommString, sqlConn);
metaDataComm.CommandType = CommandType.StoredProcedure;
SqlParameter sp = metaDataComm.Parameters.Add("@template",SqlDbType.VarChar,50);
sp.Value = Template;

SqlDataReader metadr = metaDataComm.ExecuteReader();

我的存储过程是:

   USE [QCApp]
   GO
   SET ANSI_NULLS ON
   GO
   SET QUOTED_IDENTIFIER ON
   GO

   ALTER PROCEDURE [dbo].[ColumnSeek] 
       @template varchar(50)
   AS
   EXEC('SELECT Column_Name, Data_Type 
   FROM [QCApp].[INFORMATION_SCHEMA].[COLUMNS] 
   WHERE TABLE_NAME = ' + @template);

我正在尝试找出我在这里做错了什么。

编辑:事实证明,模板为空,因为我从通过URL传递的参数获取其值,并且搞砸了URL参数传递(我使用@for和代替&


很老的问题,但是我遇到了同样的问题,就我而言,我没有看到我在@parameter之一中添加了额外的空间。一小时的调试。
莱昂佩尔蒂埃

请参见stackoverflow.com/a/26374810/1860652,以执行存储过程并获取此错误
AlexFoxGill 2014年

无论出于何种原因,今天都将其刊登在头版。但是,如果“模板”值来自客户端URL,这听起来很容易受到SQL注入的攻击!至少我建议使用QUOTENAME(@template)
Mark Sowul

Answers:


85

我将检查我的应用程序代码,看看将@template设置为什么值。我怀疑它为null,这就是问题所在。


是的,Template为空,我忘了设置。
托尼·彼得森

35
我能补充一下吗?DbNull是C#的
唯一

295

除了这里的其他答案,如果您忘记输入:

cmd.CommandType = CommandType.StoredProcedure;

然后,您还将收到此错误。


如果要从Visual Studio进行调试,请执行以下操作:在报表的“数据”选项卡[布局和预览选项卡旁边]所选数据集名称的旁边,还有另一个下拉控件,可用于更改CommandType。请享用!
SarjanWebDev 2012年

2
是的,SqlException很奇怪-它告诉您它知道它是一个过程,但是您必须设置它的CommandType属性以告诉它它是一个过程!
塔希尔·哈桑

@Tahir,我认为错误更多是使用“过程”作为通用术语(如添加“或函数”所暗示的),而不是暗示它知道意图是SQL DB存储过程。
Brian

3
我想象这是99%的人的解决方案
Jonesopolis

该死,为什么解决方案这么容易。谢谢。我知道该参数存在并且不为null,这就是全部。
BornToDoStuff

27

实际上,此问题通常是由如上所述的HLGEM将参数值设置为null引起的。我以为我会详细说明一些解决该问题的方法,这些方法对于我这个问题的新人们来说是有用的。

我更喜欢的解决方案是将存储过程参数默认为NULL(或您想要的任何值),上面的sangram提到了该方法,但是由于答案很冗长,因此可能会遗漏它。类似于以下内容:

CREATE PROCEDURE GetEmployeeDetails
    @DateOfBirth    DATETIME = NULL,
    @Surname        VARCHAR(20),
    @GenderCode     INT = NULL,
AS

这意味着,如果在某些情况下最终将代码中的参数设置为null,.NET将不会设置该参数,然后存储过程将使用其已定义的默认值。如果您真的想用代码解决问题,另一种解决方案是使用扩展方法为您解决问题,例如:

public static SqlParameter AddParameter<T>(this SqlParameterCollection parameters, string parameterName, T value) where T : class
{
    return value == null ? parameters.AddWithValue(parameterName, DBNull.Value) : parameters.AddWithValue(parameterName, value);
}

马特·汉密尔顿(Matt Hamilton)在这里有一篇不错的文章其中列出了处理此领域时一些更出色的扩展方法。


12

我将整数参数提供0时出现错误。并发现:

cmd.Parameters.AddWithValue("@Status", 0);

可以,但是不能:

cmd.Parameters.Add(new SqlParameter("@Status", 0));

6
第二个不起作用的原因是因为编译器认为您正在调用SqlParameter构造函数的(string,SqlDbType)重载。请参阅此处的备注。
基思

1
如果您想使用Add语法,或者您正在为命令使用对象初始化程序,则可以使用命名参数:cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
user888734

7

就我而言,我必须DBNULL.Value从代码中传递(使用if else条件)未定义null但值是的 存储过程参数null


5

调用存储过程时遇到类似问题

CREATE PROCEDURE UserPreference_Search
    @UserPreferencesId int,
    @SpecialOfferMails char(1),
    @NewsLetters char(1),
    @UserLoginId int,
    @Currency varchar(50)
AS
DECLARE @QueryString nvarchar(4000)

SET @QueryString = 'SELECT UserPreferencesId,SpecialOfferMails,NewsLetters,UserLoginId,Currency FROM UserPreference'
IF(@UserPreferencesId IS NOT NULL)
BEGIN
SET @QueryString = @QueryString + ' WHERE UserPreferencesId = @DummyUserPreferencesId';
END

IF(@SpecialOfferMails IS NOT NULL)
BEGIN
SET @QueryString = @QueryString + ' WHERE SpecialOfferMails = @DummySpecialOfferMails';
END

IF(@NewsLetters IS NOT NULL)
BEGIN
SET @QueryString = @QueryString + ' WHERE NewsLetters = @DummyNewsLetters';
END

IF(@UserLoginId IS NOT NULL)
BEGIN
SET @QueryString = @QueryString + ' WHERE UserLoginId = @DummyUserLoginId';
END

IF(@Currency IS NOT NULL)
BEGIN
SET @QueryString = @QueryString + ' WHERE Currency = @DummyCurrency';
END

EXECUTE SP_EXECUTESQL @QueryString
                     ,N'@DummyUserPreferencesId int, @DummySpecialOfferMails char(1), @DummyNewsLetters char(1), @DummyUserLoginId int, @DummyCurrency varchar(50)'
                     ,@DummyUserPreferencesId=@UserPreferencesId
                     ,@DummySpecialOfferMails=@SpecialOfferMails
                     ,@DummyNewsLetters=@NewsLetters
                     ,@DummyUserLoginId=@UserLoginId
                     ,@DummyCurrency=@Currency;

我通过以下哪种方法动态构造了搜索查询:

public DataSet Search(int? AccessRightId, int? RoleId, int? ModuleId, char? CanAdd, char? CanEdit, char? CanDelete, DateTime? CreatedDatetime, DateTime? LastAccessDatetime, char? Deleted)
    {
        dbManager.ConnectionString = ConfigurationManager.ConnectionStrings["MSSQL"].ToString();
        DataSet ds = new DataSet();
        try
        {
            dbManager.Open();
            dbManager.CreateParameters(9);
            dbManager.AddParameters(0, "@AccessRightId", AccessRightId, ParameterDirection.Input);
            dbManager.AddParameters(1, "@RoleId", RoleId, ParameterDirection.Input);
            dbManager.AddParameters(2, "@ModuleId", ModuleId, ParameterDirection.Input);
            dbManager.AddParameters(3, "@CanAdd", CanAdd, ParameterDirection.Input);
            dbManager.AddParameters(4, "@CanEdit", CanEdit, ParameterDirection.Input);
            dbManager.AddParameters(5, "@CanDelete", CanDelete, ParameterDirection.Input);
            dbManager.AddParameters(6, "@CreatedDatetime", CreatedDatetime, ParameterDirection.Input);
            dbManager.AddParameters(7, "@LastAccessDatetime", LastAccessDatetime, ParameterDirection.Input);
            dbManager.AddParameters(8, "@Deleted", Deleted, ParameterDirection.Input);
            ds = dbManager.ExecuteDataSet(CommandType.StoredProcedure, "AccessRight_Search");
            return ds;
        }
        catch (Exception ex)
        {
        }
        finally
        {
            dbManager.Dispose();
        }
        return ds;
    }

然后,经过大量的抓挠,我将存储过程修改为:

ALTER PROCEDURE [dbo].[AccessRight_Search]
    @AccessRightId int=null,
    @RoleId int=null,
    @ModuleId int=null,
    @CanAdd char(1)=null,
    @CanEdit char(1)=null,
    @CanDelete char(1)=null,
    @CreatedDatetime datetime=null,
    @LastAccessDatetime datetime=null,
    @Deleted char(1)=null
AS
DECLARE @QueryString nvarchar(4000)
DECLARE @HasWhere bit
SET @HasWhere=0

SET @QueryString = 'SELECT a.AccessRightId, a.RoleId,a.ModuleId, a.CanAdd, a.CanEdit, a.CanDelete, a.CreatedDatetime, a.LastAccessDatetime, a.Deleted, b.RoleName, c.ModuleName FROM AccessRight a, Role b, Module c WHERE a.RoleId = b.RoleId AND a.ModuleId = c.ModuleId'

SET @HasWhere=1;

IF(@AccessRightId IS NOT NULL)
    BEGIN
        IF(@HasWhere=0) 
            BEGIN
                SET @QueryString = @QueryString + ' WHERE a.AccessRightId = @DummyAccessRightId';
                SET @HasWhere=1;
            END
        ELSE                SET @QueryString = @QueryString + ' AND a.AccessRightId = @DummyAccessRightId';
    END

IF(@RoleId IS NOT NULL)
    BEGIN
        IF(@HasWhere=0)
            BEGIN   
                SET @QueryString = @QueryString + ' WHERE a.RoleId = @DummyRoleId';
                SET @HasWhere=1;
            END
        ELSE            SET @QueryString = @QueryString + ' AND a.RoleId = @DummyRoleId';
    END

IF(@ModuleId IS NOT NULL)
BEGIN
    IF(@HasWhere=0) 
            BEGIN   
                SET @QueryString = @QueryString + ' WHERE a.ModuleId = @DummyModuleId';
                SET @HasWhere=1;
            END
    ELSE SET @QueryString = @QueryString + ' AND a.ModuleId = @DummyModuleId';
END

IF(@CanAdd IS NOT NULL)
BEGIN
    IF(@HasWhere=0) 
            BEGIN       
                SET @QueryString = @QueryString + ' WHERE a.CanAdd = @DummyCanAdd';
                SET @HasWhere=1;
            END
    ELSE SET @QueryString = @QueryString + ' AND a.CanAdd = @DummyCanAdd';
END

IF(@CanEdit IS NOT NULL)
BEGIN
    IF(@HasWhere=0) 
        BEGIN
            SET @QueryString = @QueryString + ' WHERE a.CanEdit = @DummyCanEdit';
            SET @HasWhere=1;
        END
    ELSE SET @QueryString = @QueryString + ' AND a.CanEdit = @DummyCanEdit';
END

IF(@CanDelete IS NOT NULL)
BEGIN
    IF(@HasWhere=0) 
        BEGIN
            SET @QueryString = @QueryString + ' WHERE a.CanDelete = @DummyCanDelete';
            SET @HasWhere=1;
        END
    ELSE SET @QueryString = @QueryString + ' AND a.CanDelete = @DummyCanDelete';
END

IF(@CreatedDatetime IS NOT NULL)
BEGIN
    IF(@HasWhere=0) 
    BEGIN
        SET @QueryString = @QueryString + ' WHERE a.CreatedDatetime = @DummyCreatedDatetime';
        SET @HasWhere=1;
    END
    ELSE SET @QueryString = @QueryString + ' AND a.CreatedDatetime = @DummyCreatedDatetime';
END

IF(@LastAccessDatetime IS NOT NULL)
BEGIN
    IF(@HasWhere=0) 
        BEGIN
            SET @QueryString = @QueryString + ' WHERE a.LastAccessDatetime = @DummyLastAccessDatetime';
            SET @HasWhere=1;
        END
    ELSE SET @QueryString = @QueryString + ' AND a.LastAccessDatetime = @DummyLastAccessDatetime';
END

IF(@Deleted IS NOT NULL)
BEGIN
  IF(@HasWhere=0)   
    BEGIN
        SET @QueryString = @QueryString + ' WHERE a.Deleted = @DummyDeleted';
        SET @HasWhere=1;
    END
  ELSE SET @QueryString = @QueryString + ' AND a.Deleted = @DummyDeleted';
END

PRINT @QueryString

EXECUTE SP_EXECUTESQL @QueryString
                      ,N'@DummyAccessRightId int, @DummyRoleId int, @DummyModuleId int, @DummyCanAdd char(1), @DummyCanEdit char(1), @DummyCanDelete char(1), @DummyCreatedDatetime datetime, @DummyLastAccessDatetime datetime, @DummyDeleted char(1)'
                      ,@DummyAccessRightId=@AccessRightId
                      ,@DummyRoleId=@RoleId
                      ,@DummyModuleId=@ModuleId
                      ,@DummyCanAdd=@CanAdd
                      ,@DummyCanEdit=@CanEdit
                      ,@DummyCanDelete=@CanDelete
                      ,@DummyCreatedDatetime=@CreatedDatetime
                      ,@DummyLastAccessDatetime=@LastAccessDatetime
                      ,@DummyDeleted=@Deleted;

在这里,我将存储过程的输入参数初始化为null,如下所示

    @AccessRightId int=null,
@RoleId int=null,
@ModuleId int=null,
@CanAdd char(1)=null,
@CanEdit char(1)=null,
@CanDelete char(1)=null,
@CreatedDatetime datetime=null,
@LastAccessDatetime datetime=null,
@Deleted char(1)=null

这对我有用。

我希望这对陷入类似陷阱的人有所帮助。


3

如果未设置模板(即== null),也会引发此错误。

更多评论:

如果在添加参数时知道参数值,则也可以使用AddWithValue

不需要EXEC。您可以直接在SELECT中引用@template参数。


0

首先-为什么是EXEC?那不应该是

AS
SELECT Column_Name, ...
FROM ...
WHERE TABLE_NAME = @template

当前的SP没有意义吗?特别是,它将查找与@template匹配的,而不是@template的varchar值。即如果@template是'Column_Name',它将搜索WHERE TABLE_NAME = Column_Name,这是非常罕见的(表和列的名称相同)。

另外,如果你必须使用动态SQL,你应该使用EXEC sp_ExecuteSQL(保持值作为参数)从注入攻击(而不是输入的连接)来防止。但这不是必需的。

重新考虑实际问题-乍一看似乎还可以;您确定没有其他的SP副本吗?这是一个常见错误...


这种更改仍然无法解决。我有执行程序,是因为我以前使用过proc,其中from子句是从param提供的,所以我对此错误地进行了思考。但是我仍然会因为选择而出错
Tony Peterson

非常好奇; 也许高音检查错别字?
Marc Gravell

0

今天,当将空值传递给存储过程的参数时,我遇到了此错误。通过添加默认值= null来更改存储过程,可以轻松解决问题。


0

我遇到了同样的问题,要解决此问题,只需将与存储过程中相同的参数名称添加到您的参数集合中即可。

假设您创建一个存储过程:

create procedure up_select_employe_by_ID 
     (@ID int) 
as
    select * 
    from employe_t 
    where employeID = @ID

因此,请确保完全按照存储过程中的名称命名该参数,这将是

cmd.parameter.add("@ID", sqltype,size).value = @ID

如果你走的话

cmd.parameter.add("@employeID", sqltype,size).value = @employeid 

然后发生错误。


0

有必要告诉您正在调用Stored Proc:

comm.CommandType = CommandType.StoredProcedure;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.