Comment utiliser DbContext.Database.SqlQuery <TElement> (sql, params) avec la procédure stockée? Code EF First CTP5

250

J'ai une procédure stockée qui a trois paramètres et j'ai essayé d'utiliser les éléments suivants pour renvoyer les résultats:

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

Au début, j'ai essayé d'utiliser des SqlParameterobjets comme paramètres, mais cela n'a pas fonctionné et j'ai lancé un SqlExceptionavec le message suivant:

La procédure ou la fonction 'mySpName' attend le paramètre '@ param1', qui n'a pas été fourni.

Ma question est donc de savoir comment utiliser cette méthode avec une procédure stockée qui attend des paramètres?

Merci.

mouton électrique
la source
Quelle version de SQL Server utilisez-vous? Je rencontre des problèmes avec le code qui fonctionne en 2008 en mode compat (90), mais lorsque je l'exécute contre 2005, il échoue avec une erreur de syntaxe.
Gats
4
@Gats - J'ai eu le même problème avec SQL 2005. Ajoutez "EXEC" avant le nom de la procédure stockée. J'ai posté cette information ici pour référence future: stackoverflow.com/questions/6403930/…
Dan Mork

Réponses:

389

Vous devez fournir les instances SqlParameter de la manière suivante:

context.Database.SqlQuery<myEntityType>(
    "mySpName @param1, @param2, @param3",
    new SqlParameter("param1", param1),
    new SqlParameter("param2", param2),
    new SqlParameter("param3", param3)
);
Devart
la source
3
Comment feriez-vous pour que cette méthode fonctionne avec les types nullables? J'ai essayé cela avec des décimales nulles, mais lorsque les décimales sont nulles, j'obtiens des erreurs disant que le paramètre est manquant. Cependant, la méthode ci-dessous mentionnée par @DanMork fonctionne.
Paul Johnson
2
Est-ce que passer DbNull.Valueau lieu de null résout le problème?
Alireza
29
Vous pouvez également utiliser la syntaxe \ @ p # pour éviter d'utiliser SqlParameter comme dans context.Database.SqlQuery <myEntityType ("mySpName \ @ p0, \ @ p1, \ @ p2", param1, param2, param3). Source: msdn.microsoft.com/en-US/data/jj592907 . (Remarque: a dû utiliser \ @ pour éviter les notifications des utilisateurs, doit être lu sans barre oblique inverse.)
Marco
3
Si vous utilisez des paramètres DateTime, vous devez également spécifier le type de paramètre, pas seulement le nom et la valeur. Par exemple: dbContext.Database.SqlQuery <Invoice> ("spGetInvoices @dateFrom, @dateTo", new SqlParameter {ParameterName = "dateFrom", SqlDbType = SqlDbType.DateTime, Value = startDate}, new SqlParameter "Date" ParamTo "" SqlDbType = SqlDbType.DateTime, Value = endDate}); Une autre chose importante est de respecter l'ordre des paramètres.
Francisco Goldenstein
pouvez - vous de bien vouloir vérifier ce que je fais mal , je dois vous suivez DIRECTRICE mais aucun effet stackoverflow.com/questions/27926598/...
Toxic
129

Vous pouvez également utiliser le paramètre "sql" comme spécificateur de format:

context.Database.SqlQuery<MyEntityType>("mySpName @param1 = {0}", param1)
Dan Mork
la source
J'ai dû voter pour cela. Bien qu'elle n'ait pas été acceptée comme réponse, c'est une solution beaucoup plus facile à écrire que celle sélectionnée comme réponse.
Nikkoli
10
Cette syntaxe me préoccupe un peu. Serait-il sensible à l'injection SQL? Je suppose qu'EF exécute "EXEC mySpName @ Param1 =", et qu'il serait possible d'envoyer "x 'GO [script malveillant]" et causer des problèmes?
Tom Halladay
10
@TomHalladay aucun risque d'injection SQL - la méthode citera toujours et échappera les paramètres basés sur leur type, les mêmes que les paramètres de style @. Ainsi, pour un paramètre de chaîne, vous utiliseriez "SELECT * FROM Users WHERE email = {0}" sans guillemets dans votre relevé.
Ross McNab
dans mon cas, nous avons beaucoup de paramètres facultatifs pour SP et ne fonctionnions pas avec SqlParameters mais ce format fait l'affaire, il suffit d'ajouter "EXEC" au début. Merci.
Onur Topal
1
Cette réponse est utile si vous devez spécifier des paramètres à un proc avec des paramètres facultatifs. Exemple qui ne fonctionne pas: ProcName @optionalParam1 = @opVal1, @optionalParam2 = @opVal2 Exemple qui fonctionne:ProcName @optionalParam1 = {0}, @optionalParam2 = {1}
Garrison Neely
72

Cette solution est (uniquement) pour SQL Server 2005

Vous êtes des sauveteurs, mais comme l'a dit @Dan Mork, vous devez ajouter EXEC au mix. Ce qui me faisait trébucher, c'était:

  • «EXEC» avant le nom du proc
  • Des virgules entre les paramètres
  • Couper '@' dans les définitions de paramètres (pas sûr que ce bit soit requis cependant).

:

context.Database.SqlQuery<EntityType>(
    "EXEC ProcName @param1, @param2", 
    new SqlParameter("param1", param1), 
    new SqlParameter("param2", param2)
);
Tom Halladay
la source
21
+1. Aucune des réponses les plus votées ne comprend exec, mais je peux confirmer que j'obtiens une exception si je l'omet.
Jordan Grey
Merci, je recevais une erreur, a ajouté EXEC et l'erreur a disparu. La partie bizarre était si je faisais context.Database.SqlQuery <EntityType> ("ProcName '" + param1 + "', '" + param2 + "'"); cela a fonctionné, mais si j'ai ajouté des paramètres, cela n'a pas fonctionné jusqu'à ce que j'ajoute le mot clé EXEC.
Solmead
2
FYI: Je n'ai pas besoin du execmot - clé. +1 pour la suppression du @ sur les paramètres, ça me dérange toujours.
Nathan Koop
+1, je manquais EXEC et continuais à recevoir des SqlExceptions avec un message: syntaxe incorrecte près de 'procName'.
A. Murray
1
@Ziggler êtes-vous en 2005 ou plus récent? Le mot-clé EXEC a été principalement un problème pour ceux d'entre nous qui vont contre 2005.
Tom Halladay
15
return context.Database.SqlQuery<myEntityType>("mySpName {0}, {1}, {2}",
new object[] { param1, param2, param3 });

//Ou

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

//Ou

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

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

//Ou

using(var context = new MyDataContext())
{  
return context.Database.SqlQuery<myEntityType>("mySpName {0}, {1}, {2}",
param1, param2, param3).ToList();
}
Thulasiram
la source
cela fonctionne pour moi pour Assembly EntityFramework.dll, v4.4.0.0
Thulasiram
2
si vous utilisez en utilisant (var context = new MyDataContext ()) alors .ToList () est obligatoire.
Thulasiram
J'ai passé pas mal de temps à découvrir que .ToList () est obligatoire pour obtenir un ensemble de résultats correct.
Halim
8

La plupart des réponses sont fragiles car elles dépendent de l'ordre des paramètres du SP. Mieux vaut nommer les paramètres du Proc stocké et leur donner des valeurs paramétrées.

Afin d'utiliser les paramètres Named lors de l'appel de votre SP, sans vous soucier de l'ordre des paramètres

Utilisation de paramètres nommés SQL Server avec ExecuteStoreQuery et ExecuteStoreCommand

Décrit la meilleure approche. Mieux que la réponse de Dan Mork ici.

  • Ne repose pas sur la concaténation de chaînes et ne dépend pas de l'ordre des paramètres définis dans le SP.

Par exemple:

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
la source
Il semble que "params" soit un mot-clé réservé, donc je ne pense pas que vous puissiez l'utiliser comme ça. Sinon, c'était une réponse utile pour moi. Merci!
ooXei1sh
@ ooXei1sh - fixe, utilisant une sqlParamsvariable
Don Cheadle
vous pouvez le préfixer avec @ pour utiliser un mot réservé, mais vous ne devriez
StingyJack
6
db.Database.SqlQuery<myEntityType>("exec GetNewSeqOfFoodServing @p0,@p1,@p2 ", foods_WEIGHT.NDB_No, HLP.CuntryID, HLP.ClientID).Single()

ou

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

ou

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)

ou

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

J'utilise cette méthode:

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

Je l'aime parce que je viens de déposer Guids et Datetimes et SqlQuery effectue tout le formatage pour moi.

Malcolm O'Hare
la source
1

La réponse de @Tom Halladay est correcte avec la mention que vous devez également vérifier les valeurs nulles et envoyer DbNullable si les paramètres sont nuls car vous obtiendriez une exception comme

La requête paramétrée '...' attend le paramètre '@parameterName', qui n'a pas été fourni.

Quelque chose comme ça m'a aidé

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

(le crédit de la méthode va à https://stackoverflow.com/users/284240/tim-schmelter )

Ensuite, utilisez-le comme:

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

ou une autre solution, plus simple, mais pas générique serait:

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

J'ai eu le même message d'erreur lorsque je travaillais avec l'appel d'une procédure stockée qui prend deux paramètres d'entrée et renvoie 3 valeurs à l'aide de l'instruction SELECT et j'ai résolu le problème comme ci-dessous dans 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; }
}

MISE À JOUR : Il semble qu'avec SQL SERVER 2005, le mot-clé EXEC manquant crée un problème. Donc, pour lui permettre de fonctionner avec toutes les versions de SQL SERVER, j'ai mis à jour ma réponse et ajouté EXEC dans la ligne ci-dessous

 List<Sample> x = this.Database.SqlQuery<Sample>(" EXEC usp_NextNumberBOGetMulti @TableName, @FieldName", param).ToList();
Ziggler
la source
Veuillez voir le lien ci-dessous. Il n'est pas nécessaire d'utiliser exec msdn.microsoft.com/en-us/data/jj592907.aspx
Ziggler
0

J'ai fait le mien avec EF 6.x comme ceci:

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


            }

Ne doublez pas le paramètre sql, certaines personnes se brûlent en faisant cela à leur variable

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