Процедура очікує параметр, який не був наданий


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-параму (я використовував @для і замість &)


Дуже давнє запитання, але я зіткнувся з тією ж проблемою, і в моєму випадку я не побачив, що я додав додаткового місця в одному з @parameter. Одна година налагодження.
Леон Пелтьє

Див. Stackoverflow.com/a/26374810/1860652 про виконання збережених процедур та отримання цієї помилки
AlexFoxGill

Сьогодні це з’явилося на головній сторінці з будь-якої причини. Але це здається, що це вразливо для ін'єкції SQL, якщо значення "шаблон" надходить з клієнтської URL-адреси! Принаймні, я б запропонував використовуватиQUOTENAME(@template)
Марк Совул

Відповіді:


85

Я перевірив би свій код програми і побачив, яке значення ви встановлюєте @template. Я підозрюю, що це недійсно, і в цьому криється проблема.


Так, шаблон був недійсним, я забув його встановити раніше.
Тоні Петерсон

35
Чи можу я лише додати, що DbNull є єдиною найкориснішою "особливістю" C #
thaBadDawg

295

Окрім інших відповідей тут, якщо ви забули поставити:

cmd.CommandType = CommandType.StoredProcedure;

Тоді ви також отримаєте цю помилку.


Якщо ви налагоджуєте його з Visual Studio: На вкладці даних звіту [поруч із макетом та вкладками попереднього перегляду] поруч із назвою вибраного набору даних, є ще один спадний контроль, який дозволяє змінити CommandType. Насолоджуйтесь!
SarjanWebDev

2
так, SqlException дивна - це говорить вам, що він знає це як процедуру, але тоді вам потрібно встановити його властивість CommandType, щоб сказати, що це процедура!
Тахір Хассан

@Tahir, я думаю, що помилка полягає в тому, що використовується "процедура" як загальний термін (як це пропонується додаванням "або функції"), а не це означає, що він має намір зберігати процедуру SQL DB.
Брайан

3
це рішення для 99% людей, які приїжджають сюди, я думаю,
Jonesopolis

Блін, чому рішення було таким простим. Дякую. Я знав, що параметр існує і не був нульовим, і це все, що потрібно.
BornToDoStuff

27

Ця проблема справді зазвичай викликана встановленням значення параметра в нульове значення, як згадувалося вище HLGEM . Я думав, що я буду детально розглянути деякі рішення цієї проблеми, які я вважаю корисними для користі для нових людей.

Я вважаю за краще рішення - за замовчуванням збережених параметрів процедури до NULL (або будь-якого значення, яке ви хочете), яке було зазначено в Санграмі вище, але воно може бути пропущено, оскільки відповідь дуже багатослівний. Щось у напрямку:

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

Це означає, що якщо параметр закінчується встановленням у коді до нуля при деяких умовах. Іншим рішенням, якщо ви дійсно хочете вирішити проблему в коді, було б використовувати метод розширення, який вирішує проблему для вас, наприклад:

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
Причина, що друга не працює, полягає в тому, що компілятор вважає, що ви викликаєте (рядок, SqlDbType) перевантаження конструктора SqlParameter. Дивіться зауваження тут .
Кіт

1
Якщо ви хочете використовувати Addсинтаксис, або ви використовували ініціалізатор об'єктів для своєї команди, ви можете використовувати названий параметр:cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
user888734

7

У моєму випадку мені довелося передати DBNULL.Value(використовуючи, якщо інша умова) параметр з коду для збережених процедур, параметр, який не визначений, 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;

ТУТ я ініціалізую параметри введення збереженої процедури, щоб нульове було як наступне

    @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 не потрібно. Ви можете посилатися на параметр @template безпосередньо в SELECT.


0

По-перше - чому це EXEC? Чи не повинно так бути

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

Чинне SP не має сенсу? Зокрема, це шукало б колонку відповідає @template, а не значення varchar @template. тобто, якщо @template є 'Column_Name', він буде шукати WHERE TABLE_NAME = Column_Name, що дуже рідко (щоб таблиця та стовпець були названі однаковими).

Також, якщо ви робите повинні використовувати динамічний SQL, ви повинні використовувати EXEC sp_ExecuteSQL(зберігаючи значення в якості параметрів) для запобігання атак ін'єкцій (а не конкатенація входу). Але це не потрібно в цьому випадку.

Повторіть актуальну проблему - це здається добре з першого погляду; ви впевнені, що у вас немає іншої копії ІП? Це поширена помилка ...


Це все ще не працює з цією зміною. У мене був exec, тому що я працював раніше з професіоналом, де пункт від постачальників постачався з парами, тому я неправильно думав над цим. Але я все-таки отримую помилку лише з вибору
Тоні Петерсон

дуже цікаво; можливо, перевіряти трибровність на помилки друку?
Марк Гравелл

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

Потрібно сказати, що виклик зберігається:

comm.CommandType = CommandType.StoredProcedure;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.