โพรซีเดอร์ต้องการพารามิเตอร์ที่ไม่ได้ให้มา


106

ฉันได้รับข้อผิดพลาดเมื่อเข้าถึง Stored Procedure ใน SQL Server

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

สิ่งนี้เกิดขึ้นเมื่อฉันเรียก Stored Procedure ด้วยพารามิเตอร์ผ่านการเชื่อมต่อข้อมูลของ. 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();

และ Stored Procedure ของฉันคือ:

   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 อันใดอันหนึ่ง หนึ่งชั่วโมงของการดีบัก
Léon Pelletier

ดูstackoverflow.com/a/26374810/1860652สำหรับการดำเนินการโพรซีเดอร์ที่เก็บไว้และรับข้อผิดพลาดนี้
AlexFoxGill

สิ่งนี้จบลงในหน้าแรกของวันนี้ด้วยเหตุผลใดก็ตาม แต่ดูเหมือนว่าจะเสี่ยงต่อการแทรก SQL หากค่า "template" มาจาก URL ของไคลเอ็นต์! อย่างน้อยที่สุดฉันขอแนะนำให้ใช้QUOTENAME(@template)
Mark Sowul

คำตอบ:


85

ฉันจะตรวจสอบรหัสแอปพลิเคชันของฉันและดูว่าคุณตั้งค่า @template เป็นค่าใด ฉันสงสัยว่ามันเป็นโมฆะและมีปัญหาอยู่ในนั้น


ใช่เทมเพลตเป็นโมฆะฉันลืมตั้งค่าไว้ก่อนหน้านี้
Tony Peterson

35
ฉันขอเพิ่มได้ไหมว่า DbNull เป็น "ฟีเจอร์" ที่ไร้ประโยชน์ที่สุดของ C #
thaBadDawg

295

นอกเหนือจากคำตอบอื่น ๆ ที่นี่หากคุณลืมใส่:

cmd.CommandType = CommandType.StoredProcedure;

จากนั้นคุณจะได้รับข้อผิดพลาดนี้


ถ้าคุณกำลังดีบักจาก Visual Studio: บนแท็บข้อมูลของรายงาน [ข้างเลย์เอาต์และแท็บแสดงตัวอย่าง] ถัดจากชื่อของชุดข้อมูลที่เลือกจะมีตัวควบคุมแบบเลื่อนลงอีกตัวที่ช่วยให้คุณเปลี่ยน CommandType ได้ สนุก!
SarjanWebDev

2
ใช่ SqlException นั้นแปลก - มันบอกคุณว่ามันรู้ว่าเป็นโพรซีเดอร์ แต่คุณต้องตั้งค่าคุณสมบัติ CommandType เพื่อบอกว่ามันเป็นโพรซีเดอร์!
Tahir Hassan

@Tahir ฉันคิดว่าข้อผิดพลาดกำลังใช้ "ขั้นตอน" เป็นคำทั่วไปมากกว่า (ตามที่แนะนำโดยการเพิ่ม "หรือฟังก์ชัน") แทนที่จะบอกเป็นนัยว่าทราบว่าเจตนาคือขั้นตอนการจัดเก็บ SQL DB
Brian

3
นี่คือทางออกสำหรับ 99% ของผู้คนที่มาที่นี่ฉันคิดว่า
Jonesopolis

ทำไมวิธีแก้ปัญหาถึงง่ายขนาดนี้ ขอบคุณ. ฉันรู้ว่าพารามิเตอร์มีอยู่และไม่เป็นโมฆะและทั้งหมดนี้ก็ใช้เวลา
BornToDoStuff

27

ปัญหานี้มักเกิดจากการตั้งค่าพารามิเตอร์เป็นค่าว่างตามที่HLGEM ได้กล่าวไว้ข้างต้น ฉันคิดว่าฉันจะอธิบายรายละเอียดเกี่ยวกับวิธีแก้ปัญหานี้ซึ่งฉันพบว่ามีประโยชน์เพื่อประโยชน์ของผู้ที่เพิ่งเริ่มใช้ปัญหานี้

วิธีแก้ปัญหาที่ฉันชอบคือการตั้งค่าพารามิเตอร์ขั้นตอนที่เก็บไว้เป็นค่าเริ่มต้นเป็น 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
สาเหตุที่ตัวที่ 2 ไม่ทำงานเนื่องจากคอมไพเลอร์คิดว่าคุณกำลังเรียกใช้(string, SqlDbType)มากเกินไปของตัวสร้าง SqlParameter ดูหมายเหตุที่นี่
Keith

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 (การรักษาค่าเป็นพารามิเตอร์) เพื่อป้องกันจากการโจมตีฉีด (แทนที่จะเรียงต่อกันของการป้อนข้อมูล) แต่ไม่จำเป็นในกรณีนี้

ถึงปัญหาที่เกิดขึ้นจริง - มันดูดีจากการเหลือบมอง คุณแน่ใจหรือไม่ว่าคุณไม่มี SP สำเนาอื่นอยู่รอบ ๆ ? นี่เป็นข้อผิดพลาดทั่วไป ...


มันยังใช้ไม่ได้กับการเปลี่ยนแปลงนั้น ฉันมีผู้บริหารเพราะฉันเคยทำงานกับ proc มาก่อนซึ่งมีการจัดหา from clause จาก param ดังนั้นฉันจึงคิดผิดกับอันนี้ แต่ฉันยังคงได้รับข้อผิดพลาดเพียงแค่เลือก
Tony Peterson

อยากรู้มาก; บางทีเสียงแหลม - ตรวจสอบการพิมพ์ผิด?
Marc Gravell

0

วันนี้ฉันเจอข้อผิดพลาดนี้เมื่อค่า null ถูกส่งไปยังพารามิเตอร์ของกระบวนงานที่เก็บไว้ของฉัน ฉันสามารถแก้ไขได้อย่างง่ายดายโดยการแก้ไขขั้นตอนการจัดเก็บโดยการเพิ่มค่าเริ่มต้น = 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;

0

ฉันเจอปัญหาเดียวกัน และพารามิเตอร์ของฉันมีค่า null ดังนั้นฉันจึงแก้ไขโดยจัดการค่า null หากมีคนไม่แน่ใจว่าค่ารันไทม์และต้องการจัดการ null ให้ใช้สิ่งนี้ (และคุณไม่ต้องการเปลี่ยน SP / ฟังก์ชัน) เช่น

sp.Value = Template ?? (object)DBNull.Value;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.