¿Cómo utilizar DbContext.Database.SqlQuery <TElement> (sql, params) con el procedimiento almacenado? Código EF Primero CTP5

250

Tengo un procedimiento almacenado que tiene tres parámetros y he estado tratando de usar lo siguiente para devolver los resultados:

context.Database.SqlQuery<myEntityType>("mySpName", param1, param2, param3);

Al principio intenté usar SqlParameterobjetos como parámetros, pero esto no funcionó y arrojé un SqlExceptionmensaje con el siguiente mensaje:

El procedimiento o la función 'mySpName' espera el parámetro '@ param1', que no se proporcionó.

Entonces, mi pregunta es ¿cómo puede usar este método con un procedimiento almacenado que espera parámetros?

Gracias.

oveja electrica
fuente
¿Qué versión de SQL Server estás usando? Tengo problemas con el código que funciona en 2008 en modo compat (90), pero cuando lo ejecuto contra 2005 falla con un error de sintaxis.
Gats
44
@Gats: tuve el mismo problema con SQL 2005. Agregue "EXEC" antes del nombre del procedimiento almacenado. Publiqué esta información aquí para referencia futura: stackoverflow.com/questions/6403930/…
Dan Mork

Respuestas:

389

Debe proporcionar las instancias de SqlParameter de la siguiente manera:

context.Database.SqlQuery<myEntityType>(
    "mySpName @param1, @param2, @param3",
    new SqlParameter("param1", param1),
    new SqlParameter("param2", param2),
    new SqlParameter("param3", param3)
);
Devastar
fuente
3
¿Cómo haría que este método funcionara con tipos anulables? Intenté esto con decimales anulables, pero cuando los decimales son nulos, recibo errores que dicen que falta el parámetro. Sin embargo, el siguiente método mencionado por @DanMork funciona.
Paul Johnson
2
¿Pasar en DbNull.Valuelugar de nulos resuelve el problema?
Alireza
29
También puede usar la sintaxis \ @ p # para evitar el uso de SqlParameter como en contexto.Database.SqlQuery <myEntityType ("mySpName \ @ p0, \ @ p1, \ @ p2", param1, param2, param3). Fuente: msdn.microsoft.com/en-US/data/jj592907 . (Nota: tenía que usar \ @ para evitar las notificaciones de los usuarios, debería leerse sin la barra invertida).
Marco
3
Si está utilizando parámetros DateTime, también debe especificar el tipo de parámetro, no solo el nombre y el valor. Por ejemplo: dbContext.Database.SqlQuery <Invoice> ("spGetInvoices @dateFrom, @dateTo", nuevo SqlParameter {ParameterName = "dateFrom", SqlDbType = SqlDbType.DateTime, Value = startDate}, new SqlParameter {ParameterName = ", SqlDbType = SqlDbType.DateTime, Value = endDate}); Otra cosa importante es respetar el orden de los parámetros.
Francisco Goldenstein
¿puede verificar amablemente lo que estoy haciendo mal? Le he seguido la guía pero no tiene ningún efecto stackoverflow.com/questions/27926598/…
Tóxico
129

Además, puede usar el parámetro "sql" como un especificador de formato:

context.Database.SqlQuery<MyEntityType>("mySpName @param1 = {0}", param1)
Dan Mork
fuente
Tuve que votar esto. Si bien no se aceptó como respuesta, es una solución mucho más fácil de escribir que la seleccionada como respuesta.
Nikkoli
10
Esta sintaxis me preocupa un poco. ¿Sería susceptible a la inyección de SQL? Supongo que EF está ejecutando "EXEC mySpName @ Param1 =", y sería posible enviar "x 'GO [script malicioso]" y causar algunos problemas.
Tom Halladay el
10
@TomHalladay no tiene riesgo de inyección SQL: el método seguirá entrecomillando y escapando a los parámetros en función de su tipo, al igual que los parámetros de estilo @. Entonces, para un parámetro de cadena, usaría "SELECT * FROM Users WHERE email = {0}" sin comillas en su declaración.
Ross McNab
en mi caso tenemos muchos parámetros opcionales para SP y no funcionaban las llamadas con SqlParameters, pero este formato funciona, solo tenía que agregar 'EXEC' al principio. Gracias.
Onur Topal
1
Esta respuesta es útil si tiene que especificar parámetros a un proceso con parámetros opcionales. Ejemplo que no funciona: ProcName @optionalParam1 = @opVal1, @optionalParam2 = @opVal2 Ejemplo que sí funciona:ProcName @optionalParam1 = {0}, @optionalParam2 = {1}
Garrison Neely
72

Esta solución es (solo) para SQL Server 2005

Ustedes son salvavidas, pero como dijo @Dan Mork, deben agregar EXEC a la mezcla. Lo que me estaba tropezando fue:

  • 'EXEC' antes del nombre del proceso
  • Comas entre Parámetros
  • Cortar '@' en las definiciones de parámetros (aunque no estoy seguro de que se requiera ese bit).

:

context.Database.SqlQuery<EntityType>(
    "EXEC ProcName @param1, @param2", 
    new SqlParameter("param1", param1), 
    new SqlParameter("param2", param2)
);
Tom Halladay
fuente
21
+1. Ninguna de las respuestas más votadas incluye exec, pero puedo confirmar que recibo una excepción si la omito.
Jordan Gray
Gracias, recibí un error, agregué EXEC y el error desapareció. La parte extraña fue si hice context.Database.SqlQuery <EntityType> ("ProcName '" + param1 + "', '" + param2 + "'"); funcionó, pero si agregué parámetros, no funcionó hasta que agregué la palabra clave EXEC.
Solmead
2
FYI: no necesito la execpalabra clave. +1 por la eliminación de la @ en los parámetros, eso siempre me confunde.
Nathan Koop
+1, me faltaba EXEC y seguía recibiendo SqlExceptions con el mensaje: Sintaxis incorrecta cerca de 'procName'.
A. Murray
1
@Ziggler, ¿estás en 2005 o más reciente? La palabra clave EXEC ha sido principalmente un problema para quienes nos enfrentamos a 2005.
Tom Halladay
15
return context.Database.SqlQuery<myEntityType>("mySpName {0}, {1}, {2}",
new object[] { param1, param2, param3 });

//O

using(var context = new MyDataContext())
{
return context.Database.SqlQuery<myEntityType>("mySpName {0}, {1}, {2}",
new object[] { param1, param2, param3 }).ToList();
}

//O

using(var context = new MyDataContext())
{
object[] parameters =  { param1, param2, param3 };

return context.Database.SqlQuery<myEntityType>("mySpName {0}, {1}, {2}",
parameters).ToList();
}

//O

using(var context = new MyDataContext())
{  
return context.Database.SqlQuery<myEntityType>("mySpName {0}, {1}, {2}",
param1, param2, param3).ToList();
}
Tulasiram
fuente
se está trabajando para mí por la Asamblea EntityFramework.dll, v4.4.0.0
Thulasiram
2
si está usando (var context = new MyDataContext ()), entonces .ToList () es obligatorio.
Thulasiram
Pasé una buena cantidad de tiempo para descubrir que .ToList () es obligatorio para obtener el conjunto de resultados correcto.
Halim
8

La mayoría de las respuestas son frágiles porque dependen del orden de los parámetros del SP. Es mejor nombrar los parámetros del proceso almacenado y darles valores parametrizados.

Para utilizar parámetros con nombre al llamar a su SP, sin preocuparse por el orden de los parámetros

Uso de parámetros con nombre de SQL Server con ExecuteStoreQuery y ExecuteStoreCommand

Describe el mejor enfoque. Mejor que la respuesta de Dan Mork aquí.

  • No se basa en cadenas de concatenación y no se basa en el orden de los parámetros definidos en el SP.

P.ej:

var cmdText = "[DoStuff] @Name = @name_param, @Age = @age_param";
var sqlParams = new[]{
   new SqlParameter("name_param", "Josh"),
   new SqlParameter("age_param", 45)
};

context.Database.SqlQuery<myEntityType>(cmdText, sqlParams)
Don cheadle
fuente
Parece que "params" es una palabra clave reservada, así que no creo que pueda usarla así. De lo contrario, esta fue una respuesta útil para mí. ¡Gracias!
ooXei1sh
@ ooXei1sh - arreglado, usando sqlParamsvariable
Don Cheadle
se puede prefijar con @ para utilizar una palabra reservada, pero que realmente no debería
StingyJack
6
db.Database.SqlQuery<myEntityType>("exec GetNewSeqOfFoodServing @p0,@p1,@p2 ", foods_WEIGHT.NDB_No, HLP.CuntryID, HLP.ClientID).Single()

o

db.Database.SqlQuery<myEntityType>(
    "exec GetNewSeqOfFoodServing @param1, @param2", 
    new SqlParameter("param1", param1), 
    new SqlParameter("param2", param2)
);

o

var cmdText = "exec [DoStuff] @Name = @name_param, @Age = @age_param";
var @params = new[]{
   new SqlParameter("name_param", "Josh"),
   new SqlParameter("age_param", 45)
};

db.Database.SqlQuery<myEntityType>(cmdText, @params)

o

db.Database.SqlQuery<myEntityType>("mySpName {0}, {1}, {2}",
new object[] { param1, param2, param3 }).ToList();
Hossein Hajizadeh
fuente
3

Yo uso este método:

var results = this.Database.SqlQuery<yourEntity>("EXEC [ent].[GetNextExportJob] {0}", ProcessorID);

Me gusta porque solo dejo caer Guids and Datetimes y SqlQuery realiza todo el formateo por mí.

Malcolm O'Hare
fuente
1

La respuesta de @Tom Halladay es correcta con la mención de que también debe verificar los valores nulos y enviar DbNullable si los parámetros son nulos, ya que obtendría una excepción como

La consulta parametrizada '...' espera el parámetro '@parameterName', que no se proporcionó.

Algo como esto me ayudó

public static object GetDBNullOrValue<T>(this T val)
{
    bool isDbNull = true;
    Type t = typeof(T);

    if (Nullable.GetUnderlyingType(t) != null)
        isDbNull = EqualityComparer<T>.Default.Equals(default(T), val);
    else if (t.IsValueType)
        isDbNull = false;
    else
        isDbNull = val == null;

    return isDbNull ? DBNull.Value : (object) val;
}

(el crédito para el método va a https://stackoverflow.com/users/284240/tim-schmelter )

Luego úsalo como:

new SqlParameter("@parameterName", parameter.GetValueOrDbNull())

u otra solución, más simple, pero no genérica sería:

new SqlParameter("@parameterName", parameter??(object)DBNull.Value)
emanuel.virca
fuente
0

Recibí el mismo mensaje de error cuando estaba trabajando con una llamada a un procedimiento almacenado que toma dos parámetros de entrada y devuelve 3 valores usando la instrucción SELECT y resolví el problema como se muestra a continuación en EF Code First Approach

 SqlParameter @TableName = new SqlParameter()
        {
            ParameterName = "@TableName",
            DbType = DbType.String,
            Value = "Trans"
        };

SqlParameter @FieldName = new SqlParameter()
        {
            ParameterName = "@FieldName",
            DbType = DbType.String,
            Value = "HLTransNbr"
        };


object[] parameters = new object[] { @TableName, @FieldName };

List<Sample> x = this.Database.SqlQuery<Sample>("EXEC usp_NextNumberBOGetMulti @TableName, @FieldName", parameters).ToList();


public class Sample
{
    public string TableName { get; set; }
    public string FieldName { get; set; }
    public int NextNum { get; set; }
}

ACTUALIZACIÓN : Parece que con SQL SERVER 2005 falta la palabra clave EXEC está creando un problema. Entonces, para permitir que funcione con todas las versiones de SQL SERVER, actualicé mi respuesta y agregué EXEC en la siguiente línea

 List<Sample> x = this.Database.SqlQuery<Sample>(" EXEC usp_NextNumberBOGetMulti @TableName, @FieldName", param).ToList();
Ziggler
fuente
Por favor, consulte el siguiente enlace. No hay necesidad de usar exec msdn.microsoft.com/en-us/data/jj592907.aspx
Ziggler
0

Hice el mío con EF 6.x así:

using(var db = new ProFormDbContext())
            {
                var Action = 1; 
                var xNTID = "A239333";

                var userPlan = db.Database.SqlQuery<UserPlan>(
                "AD.usp_UserPlanInfo @Action, @NTID", //, @HPID",
                new SqlParameter("Action", Action),
                new SqlParameter("NTID", xNTID)).ToList();


            }

No duplique en el parámetro sql algunas personas se queman al hacer esto en su variable

var Action = new SqlParameter("@Action", 1);  // Don't do this, as it is set below already.
Tom Stickel
fuente