La procédure attend un paramètre qui n'a pas été fourni

106

J'obtiens l'erreur lors de l'accès à une procédure stockée dans SQL Server

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

Cela se produit lorsque j'appelle une procédure stockée avec un paramètre via la connexion de données de .net à sql (System.data.SqlClient), même si je fournis le paramètre. Voici mon code.

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

Et ma procédure stockée est:

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

J'essaie de comprendre ce que je fais de mal ici.

Edit: En fin de compte, le modèle était nul car j'obtenais sa valeur à partir d'un paramètre passé via l'URL et j'ai foiré le paramètre d'url en passant (j'utilisais @pour et au lieu de &)

Tony Peterson
la source
Question très ancienne, mais j'ai rencontré le même problème, et dans mon cas, je n'ai pas vu que j'avais ajouté un espace supplémentaire dans l'un des @paramètres. Une heure de débogage.
Léon Pelletier
Voir stackoverflow.com/a/26374810/1860652 pour exécuter des procédures stockées et obtenir cette erreur
AlexFoxGill
Cela a fini sur la première page aujourd'hui pour une raison quelconque. Mais il semble que cela soit vulnérable à l'injection SQL si la valeur "template" provient de l'URL du client! À tout le moins, je suggérerais d'utiliserQUOTENAME(@template)
Mark Sowul

Réponses:

85

Je vérifierais le code de mon application et verrais à quelle valeur vous définissez @template. Je soupçonne qu'il est nul et c'est là que réside le problème.

HLGEM
la source
Ouais, Template était nul, j'ai oublié de le définir plus tôt.
Tony Peterson
35
Puis-je simplement ajouter que DbNull est la SEULE "fonctionnalité" la plus inutile de C #
thaBadDawg
295

En plus des autres réponses ici, si vous avez oublié de mettre:

cmd.CommandType = CommandType.StoredProcedure;

Ensuite, vous obtiendrez également cette erreur.

Brian
la source
Si vous le déboguez à partir de Visual Studio: dans l'onglet de données du rapport [à côté des onglets de mise en page et d'aperçu] en regard du nom de l'ensemble de données sélectionné, il existe un autre contrôle déroulant qui vous permet de modifier le type de commande. Prendre plaisir!
SarjanWebDev
2
oui, l'exception SqlException est bizarre - elle vous dit qu'elle la connaît en tant que procédure, mais vous devez ensuite définir sa propriété CommandType pour lui dire que c'est une procédure!
Tahir Hassan
@Tahir, je pense que c'est plus que l'erreur utilise "procédure" comme terme générique (comme suggéré par l'ajout de "ou fonction"), plutôt que d'impliquer qu'il est conscient que l'intention est une procédure stockée SQL DB.
Brian
3
c'est la solution pour 99% des gens qui viennent ici j'imagine
Jonesopolis
Bon sang, pourquoi la solution était-elle aussi simple? Je vous remercie. Je savais que le paramètre existait et n'était pas nul et c'était tout ce qu'il fallait.
BornToDoStuff
27

Ce problème est en effet généralement causé par la définition d'une valeur de paramètre sur null comme HLGEM mentionné ci-dessus. J'ai pensé développer quelques solutions à ce problème que j'ai trouvées utiles pour le bénéfice des personnes qui ne connaissent pas ce problème.

La solution que je préfère est de définir par défaut les paramètres de la procédure stockée sur NULL (ou quelle que soit la valeur souhaitée), ce qui a été mentionné par sangram ci-dessus, mais peut être manquée car la réponse est très verbeuse. Quelque chose du genre:

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

Cela signifie que si le paramètre finit par être défini dans le code sur null dans certaines conditions, .NET ne définira pas le paramètre et la procédure stockée utilisera alors la valeur par défaut qu'elle a définie. Une autre solution, si vous voulez vraiment résoudre le problème dans le code, serait d'utiliser une méthode d'extension qui gère le problème pour vous, quelque chose comme:

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 a un bon article ici qui répertorie d'autres méthodes d'extension intéressantes dans ce domaine.

Xcalibur
la source
12

J'ai eu un problème où j'obtenais l'erreur lorsque j'ai fourni 0 à un paramètre entier. Et j'ai trouvé que:

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

fonctionne, mais cela ne fonctionne pas:

cmd.Parameters.Add(new SqlParameter("@Status", 0));
Anders Rune Jensen
la source
6
La raison pour laquelle le 2ème ne fonctionne pas est que le compilateur pense que vous appelez la surcharge (string, SqlDbType) du constructeur SqlParameter. Voir les remarques ici .
Keith
1
Si vous vouliez utiliser la Addsyntaxe, ou si vous utilisiez un initialiseur d'objet pour votre commande, vous pouvez utiliser un paramètre nommé:cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
user888734
7

Dans mon cas, j'ai dû passer DBNULL.Value(en utilisant la condition if else) du code pour les paramètres de procédures stockées qui ne sont pas définis nullmais que la valeur l'est null.

rafoo
la source
5

Je rencontre un problème similaire en appelant une procédure stockée

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;

Ce qui construit dynamiquement la requête de recherche que j'appelais ci-dessus par:

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

Ensuite, après beaucoup de grattage, j'ai modifié la procédure stockée pour:

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;

ICI J'initialise les paramètres d'entrée de la procédure stockée à null comme suit

    @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

cela a fait l'affaire pour moi.

J'espère que cela sera utile à quelqu'un qui tombe dans le même piège.

sangram
la source
3

Si le modèle n'est pas défini (c'est-à-dire == null), cette erreur sera également déclenchée.

Plus de commentaires:

Si vous connaissez la valeur du paramètre au moment où vous ajoutez des paramètres, vous pouvez également utiliser AddWithValue

L'EXEC n'est pas requis. Vous pouvez référencer directement le paramètre @template dans SELECT.

devio
la source
0

Premièrement, pourquoi est-ce un EXEC? Cela ne devrait-il pas être juste

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

Le SP actuel n'a pas de sens? En particulier, cela rechercherait une colonne correspondant à @template, pas à la valeur varchar de @template. c'est-à-dire que si @template est 'Column_Name', il rechercherait WHERE TABLE_NAME = Column_Name, ce qui est très rare (avoir une table et une colonne nommées de la même manière).

En outre, si vous ne devez utiliser SQL dynamique, vous devez utiliser EXEC sp_ExecuteSQL( en gardant les valeurs en tant que paramètres) pour empêcher des attaques d'injection (plutôt que concaténation de l' entrée). Mais ce n'est pas nécessaire dans ce cas.

Re le problème réel - il semble correct d'un coup d'œil; êtes-vous sûr de ne pas avoir une copie différente du SP? C'est une erreur courante ...

Marc Gravell
la source
Cela ne fonctionne toujours pas avec ce changement. J'avais l'exécutable parce que je travaillais auparavant avec un proc où la clause from était fournie par un paramètre, donc j'ai mal réfléchi à celui-ci. Mais j'obtiens toujours l'erreur avec juste la sélection
Tony Peterson
très curieux; peut-être vérifier les aigus pour les fautes de frappe?
Marc Gravell
0

Je suis tombé sur cette erreur aujourd'hui lorsque des valeurs nulles ont été transmises aux paramètres de ma procédure stockée. J'ai pu facilement corriger en modifiant la procédure stockée en ajoutant la valeur par défaut = null.

user4249282
la source
0

J'ai eu le même problème, pour le résoudre, ajoutez exactement le même nom de paramètre à votre collection de paramètres que dans vos procédures stockées.

Exemple

Disons que vous créez une procédure stockée:

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

Assurez-vous donc de nommer votre paramètre exactement tel qu'il est dans votre procédure stockée, ce serait

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

si vous allez

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

alors l'erreur se produit.

Programmeur haïtien
la source
0

Il est nécessaire de dire qu'un processus stocké est appelé:

comm.CommandType = CommandType.StoredProcedure;
pp
la source