Thủ tục mong đợi tham số không được cung cấp


106

Tôi gặp lỗi khi truy cập Thủ tục đã lưu trữ trong SQL Server

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

Điều này xảy ra khi tôi gọi một Thủ tục được lưu trữ với một tham số thông qua kết nối dữ liệu của .net với sql (System.data.SqlClient), ngay cả khi tôi đang cung cấp tham số. Đây là mã của tôi.

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();

Và Thủ tục được Lưu trữ của tôi là:

   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);

Tôi đang cố gắng tìm ra những gì tôi đang làm sai ở đây.

Chỉnh sửa: Hóa ra, Mẫu không có giá trị bởi vì tôi đang nhận giá trị của nó từ một tham số được truyền qua URL và tôi đã làm hỏng việc truyền tham số url (tôi đang sử dụng @for và thay vì &)


Câu hỏi rất cũ, nhưng tôi gặp phải vấn đề tương tự và trong trường hợp của tôi, tôi không thấy tôi đã thêm khoảng trắng vào một trong @parameter. Một giờ gỡ lỗi.
Léon Pelletier

Xem stackoverflow.com/a/26374810/1860652 để thực hiện các thủ tục được lưu trữ và gặp lỗi này
AlexFoxGill

Điều này đã kết thúc trên trang nhất ngày hôm nay vì bất cứ lý do gì. Nhưng có vẻ như điều này dễ bị chèn SQL nếu giá trị "mẫu" đến từ URL máy khách! Ít nhất tôi sẽ đề xuất sử dụngQUOTENAME(@template)
Mark Sowul

Câu trả lời:


85

Tôi sẽ kiểm tra mã ứng dụng của mình và xem bạn đang đặt @template thành giá trị nào. Tôi nghi ngờ nó là vô hiệu và trong đó nằm ở vấn đề.


Vâng, Mẫu không có giá trị, tôi đã quên đặt nó trước đó.
Tony Peterson

35
Tôi có thể chỉ cần thêm rằng DBNull là SINGLE nhất vô dụng "tính năng" của C #
thaBadDawg

295

Ngoài các câu trả lời khác ở đây, nếu bạn quên đặt:

cmd.CommandType = CommandType.StoredProcedure;

Sau đó, bạn cũng sẽ gặp lỗi này.


Nếu bạn đang gỡ lỗi nó từ Visual Studio: Trên tab dữ liệu của báo cáo [bên cạnh tab bố cục và tab Xem trước] bên cạnh tên của tập dữ liệu đã chọn, có một điều khiển thả xuống khác cho phép bạn thay đổi CommandType. Thưởng thức!
SarjanWebDev

2
vâng, SqlException thật kỳ lạ - nó cho bạn biết rằng nó biết nó như một thủ tục nhưng sau đó bạn phải đặt thuộc tính CommandType của nó để cho nó biết rằng nó là một thủ tục!
Tahir Hassan

@Tahir, tôi nghĩ rằng lỗi là do sử dụng "thủ tục" như một thuật ngữ chung (như được đề xuất bằng cách thêm "hoặc hàm"), thay vì ngụ ý rằng nó biết mục đích là một thủ tục được lưu trữ trong SQL DB.
Brian

3
đây là giải pháp cho 99% số người đến đây tôi tưởng tượng
Jonesopolis

Khỉ thật, tại sao giải pháp lại dễ dàng thế này. Cảm ơn bạn. Tôi biết tham số tồn tại chứ không phải null và đây là tất cả những gì cần thiết.
BornToDoStuff

27

Sự cố này thực sự thường do đặt giá trị tham số thành null như HLGEM đã đề cập ở trên. Tôi nghĩ rằng tôi sẽ trình bày chi tiết về một số giải pháp cho vấn đề này mà tôi thấy hữu ích cho lợi ích của những người mới gặp vấn đề này.

Giải pháp mà tôi thích là mặc định các tham số thủ tục được lưu trữ thành NULL (hoặc bất kỳ giá trị nào bạn muốn), điều này đã được sangram đề cập ở trên, nhưng có thể bị bỏ qua vì câu trả lời rất dài dòng. Một cái gì đó dọc theo dòng của:

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

Điều này có nghĩa là nếu tham số kết thúc được đặt trong mã thành null trong một số điều kiện, .NET sẽ không đặt tham số và thủ tục được lưu trữ sau đó sẽ sử dụng giá trị mặc định mà nó đã xác định. Một giải pháp khác, nếu bạn thực sự muốn giải quyết vấn đề bằng mã, sẽ là sử dụng một phương pháp mở rộng để xử lý vấn đề cho bạn, chẳng hạn như:

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 có một bài viết hay ở đây liệt kê một số phương pháp mở rộng tuyệt vời hơn khi xử lý lĩnh vực này.


12

Tôi đã gặp sự cố trong đó tôi sẽ gặp lỗi khi cung cấp 0 cho một tham số số nguyên. Và nhận thấy rằng:

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

hoạt động, nhưng điều này không:

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

6
Lý do thứ 2 không hoạt động là vì trình biên dịch nghĩ rằng bạn đang gọi quá tải (string, SqlDbType) của hàm tạo SqlParameter. Xem nhận xét tại đây .
Keith

1
Nếu bạn muốn sử dụng các Addcú pháp, hoặc bạn đang sử dụng một initializer đối tượng cho lệnh của bạn, bạn có thể sử dụng một tham số có tên:cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
user888734

7

Đối với trường hợp của tôi, tôi phải chuyển DBNULL.Value(sử dụng điều kiện if else) từ mã cho tham số thủ tục được lưu trữ không được xác định nullnhưng giá trị thì có null.


5

Tôi gặp sự cố tương tự khi gọi thủ tục được lưu trữ

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;

Cấu trúc động truy vấn tìm kiếm mà tôi đã gọi ở trên bằng cách:

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;
    }

Sau đó, sau nhiều lần gãi đầu, tôi đã sửa đổi quy trình đã lưu trữ thành:

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;

TẠI ĐÂY Tôi đang khởi tạo các thông số đầu vào của thủ tục đã lưu trữ thành null như sau

    @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

Điều đó đã đánh lừa tôi.

Tôi hy vọng điều này sẽ hữu ích cho những người rơi vào cái bẫy tương tự.


3

Nếu Mẫu không được đặt (tức là == null), lỗi này cũng sẽ xuất hiện.

Nhận xét thêm:

Nếu bạn biết giá trị tham số vào thời điểm bạn thêm tham số, bạn cũng có thể sử dụng AddWithValue

EXEC không bắt buộc. Bạn có thể tham chiếu trực tiếp tham số @template trong SELECT.


0

Đầu tiên - tại sao đó là EXEC? Không nên

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

SP hiện tại không có ý nghĩa? Đặc biệt, điều đó sẽ tìm kiếm cột khớp với @template, không phải giá trị varchar của @template. tức là nếu là @template 'Column_Name', nó sẽ tìm kiếm WHERE TABLE_NAME = Column_Name, điều này rất hiếm (có bảng và cột được đặt tên giống nhau).

Ngoài ra, nếu bạn làm phải sử dụng SQL động, bạn nên sử dụng EXEC sp_ExecuteSQL(giữ các giá trị như các tham số) để ngăn chặn các cuộc tấn công tiêm (chứ không phải nối đầu vào). Nhưng nó không cần thiết trong trường hợp này.

Re vấn đề thực tế - nó có vẻ ổn trong nháy mắt; bạn có chắc là bạn không có một bản sao SP khác bị treo xung quanh không? Đây là một lỗi phổ biến ...


Nó vẫn không hoạt động với sự thay đổi đó. Tôi có giám đốc điều hành vì trước đây tôi đã làm việc với một proc mà mệnh đề from được cung cấp từ một tham số nên tôi đã nghĩ sai về điều này. Nhưng tôi vẫn gặp lỗi chỉ với lựa chọn
Tony Peterson

rất tò mò; có lẽ treble-kiểm tra lỗi chính tả?
Marc Gravell

0

Tôi đã gặp lỗi này hôm nay khi các giá trị null được chuyển cho các tham số của thủ tục được lưu trữ của tôi. Tôi có thể dễ dàng sửa chữa bằng cách thay đổi quy trình được lưu trữ bằng cách thêm giá trị mặc định = null.


0

Tôi đã gặp vấn đề tương tự, để giải quyết nó, chỉ cần thêm chính xác tên tham số vào bộ sưu tập tham số của bạn như trong các thủ tục được lưu trữ của bạn.

Thí dụ

Giả sử bạn tạo một thủ tục được lưu trữ:

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

Vì vậy, hãy đảm bảo đặt tên tham số của bạn chính xác như trong thủ tục được lưu trữ của bạn, đây sẽ là

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

Nếu bạn đi

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

thì lỗi xảy ra.


0

Cần phải nói rằng một Proc được lưu trữ đang được gọi:

comm.CommandType = CommandType.StoredProcedure;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.