El procedimiento espera un parámetro que no se proporcionó

106

Recibo el error al acceder a un procedimiento almacenado en SQL Server

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

Esto sucede cuando llamo a un procedimiento almacenado con un parámetro a través de la conexión de datos de .net a sql (System.data.SqlClient), aunque estoy proporcionando el parámetro. Aquí está mi código.

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

Y mi procedimiento almacenado es:

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

Estoy tratando de averiguar qué estoy haciendo mal aquí.

Editar: Como resultado, la plantilla era nula porque obtenía su valor de un parámetro pasado a través de la URL y arruiné el paso del parámetro de URL (estaba usando @para y en lugar de &)

Tony Peterson
fuente
Pregunta muy antigua, pero encontré el mismo problema y, en mi caso, no vi que agregué un espacio adicional en uno de los @parameter. Una hora de depuración.
Léon Pelletier
Consulte stackoverflow.com/a/26374810/1860652 para ejecutar procedimientos almacenados y obtener este error
AlexFoxGill
Esto terminó hoy en la portada por cualquier motivo. ¡Pero parece que esto es vulnerable a la inyección de SQL si el valor de la "plantilla" proviene de la URL del cliente! Como mínimo, sugeriría usarQUOTENAME(@template)
Mark Sowul

Respuestas:

85

Verificaría el código de mi aplicación y vería en qué valor está configurando @template. Sospecho que es nulo y ahí radica el problema.

HLGEM
fuente
Sí, la plantilla era nula, olvidé configurarla antes.
Tony Peterson
35
¿Puedo agregar que DbNull es la ÚNICA "característica" más inútil de C #
thaBadDawg
295

Además de las otras respuestas aquí, si olvidó poner:

cmd.CommandType = CommandType.StoredProcedure;

Entonces también obtendrá este error.

Brian
fuente
Si lo está depurando desde Visual Studio: en la pestaña de datos del informe [junto a las pestañas de diseño y Vista previa] junto al nombre del conjunto de datos seleccionado, hay otro control desplegable que le permite cambiar el tipo de comando. ¡Disfrutar!
SarjanWebDev
2
sí, la SqlException es extraña: le dice que lo conoce como un procedimiento, pero luego debe configurar su propiedad CommandType para decirle que es un procedimiento.
Tahir Hassan
@Tahir, creo que es más que el error está usando "procedimiento" como un término genérico (como sugiere la adición de "o función"), en lugar de implicar que es consciente de que la intención es un procedimiento almacenado de SQL DB.
Brian
3
esta es la solución para el 99% de las personas que vienen aquí, imagino
Jonesopolis
Maldita sea, ¿por qué la solución fue tan fácil? Gracias. Sabía que el parámetro existía y no era nulo y esto fue todo lo que necesité.
BornToDoStuff
27

De hecho, este problema generalmente se debe al establecer un valor de parámetro en nulo como HLGEM mencionado anteriormente. Pensé en desarrollar algunas soluciones a este problema que he encontrado útiles para el beneficio de personas nuevas en este problema.

La solución que prefiero es establecer de forma predeterminada los parámetros del procedimiento almacenado en NULL (o el valor que desee), que fue mencionado por sangram anteriormente, pero que se puede perder porque la respuesta es muy detallada. Algo parecido a:

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

Esto significa que si el parámetro termina estableciéndose en código como nulo bajo algunas condiciones, .NET no establecerá el parámetro y el procedimiento almacenado usará el valor predeterminado que ha definido. Otra solución, si realmente desea resolver el problema en código, sería usar un método de extensión que maneje el problema por usted, algo como:

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 tiene una buena publicación aquí que enumera algunos métodos de extensión más geniales cuando se trata de esta área.

Xcalibur
fuente
12

Tuve un problema en el que obtendría el error cuando proporcioné 0 a un parámetro entero. Y encontré que:

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

funciona, pero esto no:

cmd.Parameters.Add(new SqlParameter("@Status", 0));
Anders Rune Jensen
fuente
6
La razón por la que el segundo no funciona es porque el compilador cree que está llamando a la sobrecarga (string, SqlDbType) del constructor SqlParameter. Vea los comentarios aquí .
Keith
1
Si quisiera usar la Addsintaxis, o si estaba usando un inicializador de objeto para su comando, podría usar un parámetro con nombre:cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
user888734
7

Para mi caso, tuve que pasar DBNULL.Value(usando la condición if else) del código para el parámetro de procedimientos almacenados que no están definidos nullpero el valor sí null.

rafoo
fuente
5

Me encuentro con un problema similar al llamar al procedimiento almacenado

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;

Que construyendo dinámicamente la consulta para la búsqueda que estaba llamando arriba uno por:

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

Luego, después de rascarme mucho la cabeza, modifiqué el procedimiento almacenado para:

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;

AQUÍ estoy inicializando los parámetros de entrada del procedimiento almacenado a nulo de la siguiente manera

    @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

eso me sirvió.

Espero que esto sea útil para alguien que caiga en una trampa similar.

sangram
fuente
3

Si Plantilla no está configurada (es decir, == nula), este error también se generará.

Más comentarios:

Si conoce el valor del parámetro en el momento en que agrega parámetros, también puede usar AddWithValue

No se requiere EXEC. Puede hacer referencia al parámetro @template en SELECT directamente.

devio
fuente
0

Primero, ¿por qué es un EXEC? ¿No debería ser eso?

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

¿El SP actual no tiene sentido? En particular, eso buscaría una columna que coincida con @template, no el valor varchar de @template. es decir, si @template lo es 'Column_Name', buscaría WHERE TABLE_NAME = Column_Name, lo cual es muy raro (tener tabla y columna con el mismo nombre).

Además, si usted no tiene que utilizar SQL dinámico, se debe utilizar EXEC sp_ExecuteSQL(manteniendo los valores como parámetros) para evitar los ataques de inyección (en lugar de concatenación de entrada). Pero no es necesario en este caso.

Con respecto al problema real, se ve bien de un vistazo; ¿Estás seguro de que no tienes una copia diferente del SP? Es un error común...

Marc Gravell
fuente
Todavía no funciona con ese cambio. Tuve el ejecutivo porque estaba trabajando anteriormente con un proceso en el que la cláusula from se proporcionó desde un parámetro, por lo que pensé mal en este. Pero sigo recibiendo el error solo con la selección
Tony Peterson
muy curioso; tal vez verifique los agudos en busca de errores tipográficos?
Marc Gravell
0

Me encontré con este error hoy cuando se pasaron valores nulos a los parámetros de mi procedimiento almacenado. Pude arreglar fácilmente modificando el procedimiento almacenado agregando valor predeterminado = nulo.

usuario4249282
fuente
0

Tuve el mismo problema, para resolverlo simplemente agregue exactamente el mismo nombre de parámetro a su colección de parámetros que en sus procedimientos almacenados.

Ejemplo

Digamos que crea un procedimiento almacenado:

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

Así que asegúrese de nombrar su parámetro exactamente como está en su procedimiento almacenado, esto sería

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

si vas

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

entonces ocurre el error.

Programador haitiano
fuente
0

Es necesario indicar que se está llamando a un proceso almacenado:

comm.CommandType = CommandType.StoredProcedure;
páginas
fuente