Prosedur mengharapkan parameter yang tidak disediakan

106

Saya mendapatkan kesalahan saat mengakses Prosedur Tersimpan di SQL Server

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

Ini terjadi ketika saya memanggil Prosedur Tersimpan dengan parameter melalui koneksi data .net ke sql (System.data.SqlClient), meskipun saya menyediakan parameter. Ini kode saya.

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

Dan Prosedur Tersimpan saya adalah:

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

Saya mencoba mencari tahu apa yang saya lakukan salah di sini.

Sunting: Ternyata, Template adalah null karena saya mendapatkan nilainya dari parameter yang melewati URL dan saya mengacaukan parameter url yang lewat (saya menggunakan @for dan bukannya &)

Tony Peterson
sumber
Pertanyaan yang sangat lama, tetapi saya mengalami masalah yang sama, dan dalam kasus saya, saya tidak melihat saya menambahkan spasi ekstra di salah satu @parameter. Satu jam debugging.
Léon Pelletier
Lihat stackoverflow.com/a/26374810/1860652 untuk menjalankan prosedur yang tersimpan dan mendapatkan kesalahan ini
AlexFoxGill
Ini berakhir di halaman depan hari ini untuk alasan apa pun. Tapi sepertinya ini rentan terhadap injeksi SQL jika nilai "template" berasal dari URL klien! Setidaknya saya akan menyarankan penggunaanQUOTENAME(@template)
Mark Sowul

Jawaban:

85

Saya akan memeriksa kode aplikasi saya dan melihat nilai apa yang Anda setel @template ke. Saya menduga itu nol dan di situlah letak masalahnya.

HLGEM
sumber
Ya, Template tidak ada, saya lupa mengaturnya sebelumnya.
Tony Peterson
35
Bisakah saya menambahkan bahwa DbNull adalah "fitur" SATU-SATUNYA yang paling tidak berguna dari C #
thaBadDawg
295

Selain jawaban lain di sini, jika Anda lupa menuliskan:

cmd.CommandType = CommandType.StoredProcedure;

Kemudian Anda juga akan mendapatkan kesalahan ini.

Brian
sumber
Jika Anda men-debugnya dari Visual Studio: Pada tab data laporan [di samping tab tata letak dan Pratinjau] di samping nama kumpulan data yang dipilih, ada kontrol tarik-turun lain yang memungkinkan Anda mengubah Jenis Perintah. Nikmati!
SarjanWebDev
2
ya, SqlException itu aneh - ia memberi tahu Anda bahwa ia mengetahuinya sebagai prosedur tetapi kemudian Anda harus mengatur properti CommandType untuk memberi tahu bahwa itu adalah prosedur!
Tahir Hassan
@Tahir, saya pikir itu lebih karena kesalahan menggunakan "prosedur" sebagai istilah umum (seperti yang disarankan oleh penambahan "atau fungsi"), daripada menyiratkan itu sadar maksudnya adalah prosedur tersimpan SQL DB.
Brian
3
Saya membayangkan ini adalah solusi untuk 99% orang yang datang ke sini
Jonesopolis
Sialan, kenapa solusinya semudah ini. Terima kasih. Saya tahu parameternya ada dan bukan null dan hanya ini yang diperlukan.
BornToDoStuff
27

Masalah ini memang biasanya disebabkan oleh pengaturan nilai parameter menjadi null seperti HLGEM yang disebutkan di atas. Saya pikir saya akan menguraikan beberapa solusi untuk masalah ini yang menurut saya berguna untuk kepentingan orang-orang yang baru mengenal masalah ini.

Solusi yang saya lebih suka adalah default parameter prosedur yang tersimpan ke NULL (atau nilai apa pun yang Anda inginkan), yang disebutkan oleh sangram di atas, tetapi mungkin terlewatkan karena jawabannya sangat bertele-tele. Sesuatu di sepanjang baris:

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

Ini berarti bahwa jika parameter akhirnya disetel dalam kode menjadi nol dalam beberapa kondisi, .NET tidak akan menyetel parameter dan prosedur tersimpan kemudian akan menggunakan nilai default yang telah ditentukan. Solusi lain, jika Anda benar-benar ingin menyelesaikan masalah dalam kode, akan menggunakan metode ekstensi yang menangani masalah tersebut untuk Anda, seperti:

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 memiliki posting bagus di sini yang mencantumkan beberapa metode penyuluhan yang lebih hebat saat menangani bidang ini.

Xcalibur
sumber
12

Saya memiliki masalah di mana saya akan mendapatkan kesalahan ketika saya memberikan 0 ke param integer. Dan menemukan bahwa:

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

berfungsi, tetapi ini tidak:

cmd.Parameters.Add(new SqlParameter("@Status", 0));
Anders Rune Jensen
sumber
6
Alasan yang kedua tidak berfungsi adalah karena kompilator mengira Anda memanggil overload (string, SqlDbType) dari konstruktor SqlParameter. Lihat keterangan di sini .
Keith
1
Jika Anda ingin menggunakan Addsintaks, atau Anda menggunakan penginisialisasi objek untuk perintah Anda, Anda dapat menggunakan parameter bernama:cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
user888734
7

Untuk kasus saya, saya harus melewatkan DBNULL.Value(menggunakan kondisi jika lain) dari kode untuk parameter prosedur tersimpan yang tidak ditentukan nulltetapi nilainya null.

rafoo
sumber
5

Saya menemukan masalah serupa saat memanggil prosedur tersimpan

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;

Yang secara dinamis membangun kueri untuk penelusuran yang saya panggil di atas satu dengan:

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

Kemudian setelah banyak garukan kepala saya mengubah prosedur tersimpan menjadi:

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;

DI SINI Saya Menginisialisasi Parameter Input dari Prosedur Tersimpan ke null sebagai Berikut

    @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

itu melakukan trik untuk-Ku.

Saya harap ini akan membantu seseorang yang jatuh dalam perangkap serupa.

sangram
sumber
3

Jika Template tidak diset (yaitu == null), kesalahan ini akan dimunculkan juga.

Lebih banyak komentar:

Jika Anda mengetahui nilai parameter pada saat Anda menambahkan parameter, Anda juga dapat menggunakan AddWithValue

EXEC tidak diperlukan. Anda bisa mereferensikan parameter @template di SELECT secara langsung.

devio
sumber
0

Pertama - mengapa itu EXEC? Bukankah seharusnya begitu

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

SP saat ini tidak masuk akal? Secara khusus, itu akan mencari kolom yang cocok dengan @template, bukan nilai varchar dari @template. yaitu jika @template adalah 'Column_Name', ia akan mencari WHERE TABLE_NAME = Column_Name, yang sangat jarang (memiliki tabel dan kolom bernama sama).

Selain itu, jika Anda memang harus menggunakan SQL dinamis, Anda harus menggunakan EXEC sp_ExecuteSQL(menjaga nilai sebagai parameter) untuk mencegah serangan injeksi (bukan penggabungan input). Tapi itu tidak perlu dalam kasus ini.

Re masalah sebenarnya - sekilas terlihat OK; Anda yakin tidak memiliki salinan SP yang berbeda? Ini adalah kesalahan umum ...

Marc Gravell
sumber
Itu masih tidak berhasil dengan perubahan itu. Saya memiliki eksekutif karena saya bekerja sebelumnya dengan proc di mana klausa from dipasok dari parameter jadi saya salah berpikir tentang yang ini. Tapi saya masih mendapatkan kesalahan hanya dengan memilih
Tony Peterson
sangat penasaran; mungkin memeriksa treble untuk kesalahan ketik?
Marc Gravell
0

Saya menemukan kesalahan ini hari ini ketika nilai nol diteruskan ke parameter prosedur tersimpan saya. Saya dapat dengan mudah memperbaiki dengan mengubah prosedur yang disimpan dengan menambahkan nilai default = null.

pengguna4249282
sumber
0

Saya memiliki masalah yang sama, untuk menyelesaikannya cukup tambahkan nama parameter yang persis sama ke koleksi parameter Anda seperti dalam prosedur tersimpan Anda.

Contoh

Katakanlah Anda membuat prosedur tersimpan:

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

Jadi pastikan untuk memberi nama parameter Anda persis seperti yang ada dalam prosedur tersimpan Anda

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

Jika kau pergi

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

kemudian terjadi kesalahan.

Programmer Haiti
sumber
0

Perlu diketahui bahwa Stored Proc sedang dipanggil:

comm.CommandType = CommandType.StoredProcedure;
hal
sumber