La procedura prevede un parametro che non è stato fornito


106

Ricevo l'errore durante l'accesso a una stored procedure in SQL Server

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

Ciò accade quando chiamo una stored procedure con un parametro tramite la connessione dati di .net a sql (System.data.SqlClient), anche se sto fornendo il parametro. Ecco il mio codice.

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

E la mia procedura memorizzata è:

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

Sto cercando di capire cosa sto facendo di sbagliato qui.

Modifica: a quanto pare, Template era nullo perché stavo ottenendo il suo valore da un parametro passato attraverso l'URL e ho sbagliato il passaggio del parametro url (stavo usando @per e invece di &)


Domanda molto vecchia, ma ho riscontrato lo stesso problema e, nel mio caso, non ho visto che ho aggiunto uno spazio extra in uno dei parametri @. Un'ora di debug.
Léon Pelletier,

Vedi stackoverflow.com/a/26374810/1860652 per l'esecuzione di stored procedure e ottenere questo errore
AlexFoxGill

Questo è finito in prima pagina oggi per qualsiasi motivo. Ma sembra che questo sia vulnerabile a SQL injection se il valore "template" proviene dall'URL del client! Per lo meno suggerirei di usareQUOTENAME(@template)
Mark Sowul

Risposte:


85

Vorrei controllare il codice della mia applicazione e vedere quale valore stai impostando @template. Ho il sospetto che sia nullo e qui sta il problema.


Sì, Template era nullo, ho dimenticato di impostarlo prima.
Tony Peterson

35
Posso solo aggiungere che DbNull è la SINGOLA "funzionalità" più inutile di C #
thaBadDawg

295

Oltre alle altre risposte qui, se hai dimenticato di mettere:

cmd.CommandType = CommandType.StoredProcedure;

Quindi riceverai anche questo errore.


Se si esegue il debug da Visual Studio: nella scheda dati del report [accanto alle schede layout e Anteprima] accanto al nome del set di dati selezionato, è presente un altro controllo a discesa che consente di modificare CommandType. Godere!
SarjanWebDev

2
sì, la SqlException è strana: ti dice che la conosce come una procedura ma poi devi impostare la sua proprietà CommandType per dirgli che è una procedura!
Tahir Hassan

@ Tahir, penso che sia più che l'errore sta usando "procedura" come termine generico (come suggerito dall'aggiunta di "o funzione"), piuttosto che implicare che sia consapevole che l'intento è una stored procedure SQL DB.
Brian

3
questa è la soluzione per il 99% delle persone che vengono qui immagino
Jonesopolis

Dannazione, perché la soluzione è stata così facile. Grazie. Sapevo che il parametro esisteva e non era nullo e questo era tutto ciò che serviva.
BornToDoStuff

27

Questo problema è infatti solitamente causato dall'impostazione di un valore di parametro su null come HLGEM menzionato sopra. Ho pensato di elaborare alcune soluzioni a questo problema che ho trovato utili a beneficio di persone nuove a questo problema.

La soluzione che preferisco è impostare i parametri della stored procedure su NULL (o qualsiasi valore desideri), che è stato menzionato da sangram sopra, ma potrebbe essere perso perché la risposta è molto prolissa. Qualcosa sulla falsariga di:

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

Ciò significa che se il parametro finisce per essere impostato nel codice su null in alcune condizioni, .NET non imposterà il parametro e la procedura memorizzata utilizzerà il valore predefinito che ha definito. Un'altra soluzione, se vuoi davvero risolvere il problema nel codice, sarebbe usare un metodo di estensione che gestisca il problema per te, qualcosa come:

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 ha un buon post qui che elenca alcuni metodi di estensione più grandi quando si tratta di quest'area.


12

Ho avuto un problema in cui avrei ricevuto l'errore quando ho fornito 0 a un parametro intero. E ho scoperto che:

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

funziona, ma questo non:

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

6
Il motivo per cui il secondo non funziona è perché il compilatore pensa che tu stia chiamando l' overload (string, SqlDbType) del costruttore SqlParameter. Vedere le osservazioni qui .
Keith

1
Se si desidera utilizzare la Addsintassi o si utilizza un inizializzatore di oggetto per il comando, è possibile utilizzare un parametro denominato:cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
user888734

7

Nel mio caso, ho dovuto passare DBNULL.Value(utilizzando la condizione if else) dal codice per i parametri delle stored procedure che non sono definiti nullma il valore lo è null.


5

Mi sono imbattuto in un problema simile durante la chiamata alla stored procedure

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;

Che costruendo dinamicamente la query per la ricerca che stavo chiamando sopra uno da:

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

Quindi, dopo molti graffi alla testa, ho modificato la procedura memorizzata in:

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;

QUI sto inizializzando i parametri di input della stored procedure su null come segue

    @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

che ha fatto il trucco per me.

Spero che questo possa essere utile a qualcuno che cade in una trappola simile.


3

Se Template non è impostato (cioè == null), verrà generato anche questo errore.

Altri commenti:

Se conosci il valore del parametro quando aggiungi i parametri, puoi anche utilizzare AddWithValue

L'EXEC non è richiesto. È possibile fare riferimento al parametro @template direttamente in SELECT.


0

Primo: perché è un EXEC? Non dovrebbe essere così

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

L'attuale SP non ha senso? In particolare, ciò cercherebbe una colonna corrispondente a @template, non il valore varchar di @template. cioè, se @template è 'Column_Name', cercheràWHERE TABLE_NAME = Column_Name , il che è molto raro (avere tabella e colonna denominate allo stesso modo).

Inoltre, se non è necessario utilizzare SQL dinamico, è necessario utilizzare EXEC sp_ExecuteSQL(mantenendo i valori come parametri) per evitare attacchi di iniezione (piuttosto che concatenazione di ingresso). Ma in questo caso non è necessario.

Rispetto al problema reale: a prima vista sembra a posto; sei sicuro di non avere una copia diversa dell'SP in giro? Questo è un errore comune ...


Ancora non funziona con quel cambiamento. Avevo l'exec perché stavo lavorando in precedenza con un proc in cui la clausola from veniva fornita da un parametro, quindi ho pensato in modo sbagliato a questo. Ma ricevo ancora l'errore solo con la selezione
Tony Peterson

molto curioso; forse il controllo degli alti per errori di battitura?
Marc Gravell

0

Oggi mi sono imbattuto in questo errore quando sono stati passati valori null ai parametri della mia procedura memorizzata. Sono stato in grado di risolvere facilmente modificando la procedura memorizzata aggiungendo il valore predefinito = null.


0

Ho avuto lo stesso problema, per risolverlo è sufficiente aggiungere esattamente lo stesso nome di parametro alla raccolta di parametri come nelle stored procedure.

Esempio

Supponiamo che tu crei una stored procedure:

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

Quindi assicurati di nominare il tuo parametro esattamente come è nella tua procedura memorizzata

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

se vai

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

allora si verifica l'errore.


0

È necessario dire che viene chiamato uno Stored Proc:

comm.CommandType = CommandType.StoredProcedure;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.