Prosedür, sağlanmayan parametreyi bekliyor


106

SQL Server'da Depolanan bir Prosedüre erişirken hata alıyorum

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

Bu, parametreyi sağlıyor olmama (System.data.SqlClient)rağmen .net'in sql'ye veri bağlantısı üzerinden bir parametre içeren bir Depolanan Prosedürü çağırdığımda oluyor . İşte kodum.

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

Ve Depolanan Prosedürüm:

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

Burada neyi yanlış yaptığımı anlamaya çalışıyorum.

Düzenleme: Görünüşe göre, Şablon boştu çünkü değerini URL'den geçen bir parametreden alıyordum ve url parametresinin geçişini mahvettim ( @için ve yerine kullanıyordum &)


Çok eski bir soru, ama aynı sorunla karşılaştım ve benim durumumda @parametresinden birine fazladan boşluk eklediğimi görmedim. Bir saatlik hata ayıklama.
Léon Pelletier

Bkz stackoverflow.com/a/26374810/1860652 saklanan prosedürleri çalıştırmanın ve bu hatayı almak için
AlexFoxGill

Bu, her ne sebeple olursa olsun bugün ön sayfada sona erdi. Ancak, "şablon" değeri istemci URL'sinden geliyorsa, bunun SQL enjeksiyonuna karşı savunmasız olduğu anlaşılıyor! En azından kullanmanızı QUOTENAME(@template)
öneririm

Yanıtlar:


85

Uygulama kodumu kontrol eder ve @ şablonunu hangi değere ayarladığınızı görürdüm. Boş olduğundan şüpheleniyorum ve sorun da burada yatıyor.


Evet, Şablon boştu, daha önce ayarlamayı unuttum.
Tony Peterson


295

Buradaki diğer cevaplara ek olarak, yazmayı unuttuysanız:

cmd.CommandType = CommandType.StoredProcedure;

O zaman bu hatayı da alacaksınız.


Visual Studio'dan hata ayıklıyorsanız: Raporun veri sekmesinde [düzen ve Önizleme sekmelerinin yanında], seçilen veri kümesinin adının yanında, CommandType'ı değiştirmenize olanak tanıyan başka bir açılır denetim vardır. Zevk almak!
SarjanWebDev

2
evet, SqlException tuhaftır - size bunu bir prosedür olarak bildiğini söyler, ancak daha sonra bunun bir prosedür olduğunu söylemek için CommandType özelliğini ayarlamanız gerekir!
Tahir Hassan

@Tahir, bence hata, amacın bir SQL DB saklı yordamı olduğunun farkında olduğunu ima etmek yerine, genel bir terim olarak ("veya işlev" eklenmesi ile önerildiği gibi) "yordamı" kullanmaktır.
Brian

3
Sanırım buraya gelen insanların% 99'u için çözüm bu
Jonesopolis

Kahretsin, çözüm neden bu kadar kolaydı. Teşekkür ederim. Parametrenin var olduğunu ve boş olmadığını biliyordum ve tek gereken buydu.
BornToDoStuff

27

Bu sorun aslında genellikle bir parametre değerinin HLGEM olarak null olarak ayarlanmasından kaynaklanır. yukarıda belirtilen. Bu soruna yeni başlayan insanların yararı için yararlı bulduğum bu soruna bazı çözümler üzerinde duracağımı düşündüm.

Tercih ettiğim çözüm, yukarıda sangram tarafından bahsedilen, saklı yordam parametrelerini varsayılan olarak NULL (veya istediğiniz herhangi bir değer) olarak belirlemektir , ancak yanıt çok ayrıntılı olduğu için gözden kaçabilir . Şu çizgide bir şey:

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

Bu, parametrenin kodda bazı koşullar altında null olarak ayarlanması durumunda, .NET'in parametreyi ayarlamayacağı ve saklı yordamın daha sonra tanımladığı varsayılan değeri kullanacağı anlamına gelir. Başka bir çözüm, problemi kodda gerçekten çözmek istiyorsanız, sizin için problemi çözen bir uzatma yöntemi kullanmaktır, örneğin:

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'ın burada , bu alanla uğraşırken daha büyük genişletme yöntemlerini listeleyen iyi bir yazısı var .


12

Bir tamsayı parametresine 0 verdiğimde hatayı alacağım bir sorun yaşadım. Ve şunu buldum:

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

çalışır, ancak bu çalışmaz:

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

6
İkincisinin çalışmamasının nedeni, derleyicinin SqlParameter yapıcısının (string, SqlDbType) aşırı yüklemesini çağırdığınızı düşünmesidir . Burada açıklamalara bakın .
Keith

1
AddSözdizimini kullanmak istiyorsanız veya komutunuz için bir nesne başlatıcı kullanıyorsanız, adlandırılmış bir parametre kullanabilirsiniz:cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
user888734

7

Benim durumum DBNULL.Valueiçin, tanımlanmamış nullancak değeri olan saklı yordamlar parametresi için koddan (aksi takdirde koşulunu kullanarak) geçmek zorunda kaldım null.


5

Depolanan yordamı ararken benzer bir sorunla karşılaşıyorum

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;

Hangi arama sorgusunu dinamik olarak yukarıdan çağırdığım için:

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

Sonra çok fazla kafa kaşımasından sonra, saklı yordamı şu şekilde değiştirdim:

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;

BURADA Saklanan Prosedürün Giriş Parametrelerini Aşağıdaki Şekilde Boş Olarak Başlatıyorum

    @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

bu benim için hile yaptı.

Umarım bu, benzer tuzağa düşen birine yardımcı olur.


3

Şablon ayarlanmadıysa (yani == null), bu hata da ortaya çıkacaktır.

Daha fazla yorum:

Parametre değerini parametre eklediğiniz zamana göre biliyorsanız, AddWithValue öğesini de kullanabilirsiniz.

EXEC gerekli değildir. SELECT öğesindeki @template parametresine doğrudan başvurabilirsiniz.


0

İlk olarak - bu neden bir EXEC? Bu sadece olmamalı

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

Mevcut SP mantıklı değil mi? Özellikle, @template'in varchar değerini değil, @template ile eşleşen bir sütun arar. ör. @template ise 'Column_Name', arama yapacaktırWHERE TABLE_NAME = Column_Name , ki bu çok nadirdir (tablo ve sütunun aynı şekilde adlandırılması).

Ayrıca yaparsan dinamik SQL kullanmak zorunda, kullanmak gerekir EXEC sp_ExecuteSQLenjeksiyon saldırıları (ziyade girişin birleştirme) önlemek için (parametre olarak değerleri tutarak). Ancak bu durumda gerekli değildir.

Asıl sorun yeniden - bir bakışta iyi görünüyor; SP'nin farklı bir kopyasının ortalıkta dolaşmadığına emin misin? Bu yaygın bir hatadır ...


Hala bu değişiklikle çalışmıyor. Exec vardı çünkü daha önce from cümlesinin bir paramdan sağlandığı bir proc ile çalışıyordum, bu yüzden bu konuda yanlış düşünmeye başladım. Ama yine de hatayı sadece
seçimle

çok meraklı; belki yazım hataları için tiz kontrol?
Marc Gravell

0

Bugün bu hatayla, saklı yordamın parametrelerine boş değerler geçtiğinde karşılaştım. Varsayılan değer = null ekleyerek saklı yordamı değiştirerek kolayca düzeltebildim.


0

Ben de aynı sorunu yaşadım, çözmek için parametre koleksiyonunuza depolanan yordamlarınızda olduğu gibi tam olarak aynı parametre adını ekleyin.

Misal

Bir saklı yordam oluşturduğunuzu varsayalım:

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

Bu nedenle, parametrenizi tam olarak saklı yordamınızda olduğu gibi adlandırdığınızdan emin olun.

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

Eğer gidersen

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

sonra hata olur.


0

Stored Proc'un çağrıldığını söylemek gerekir:

comm.CommandType = CommandType.StoredProcedure;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.