Wie verwende ich DbContext.Database.SqlQuery <TElement> (sql, params) mit gespeicherter Prozedur? EF Code First CTP5

250

Ich habe eine gespeicherte Prozedur mit drei Parametern und habe versucht, Folgendes zu verwenden, um die Ergebnisse zurückzugeben:

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

Zuerst habe ich versucht, SqlParameterObjekte als Parameter zu verwenden, aber das hat nicht funktioniert und eine SqlExceptionmit der folgenden Meldung ausgegeben :

Prozedur oder Funktion 'mySpName' erwartet den Parameter '@ param1', der nicht angegeben wurde.

Meine Frage ist also, wie Sie diese Methode mit einer gespeicherten Prozedur verwenden können, die Parameter erwartet.

Vielen Dank.

Elektrikschaf
quelle
Welche Version von SQL Server verwenden Sie? Ich habe Probleme mit Code, der 2008 im kompatiblen (90) Modus funktioniert, aber wenn ich ihn gegen 2005 ausführe, schlägt er mit einem Syntaxfehler fehl.
Gats
4
@Gats - Ich hatte das gleiche Problem mit SQL 2005. Fügen Sie "EXEC" vor dem Namen der gespeicherten Prozedur hinzu. Ich habe diese Informationen hier als zukünftige Referenz veröffentlicht: stackoverflow.com/questions/6403930/…
Dan Mork

Antworten:

389

Sie sollten die SqlParameter-Instanzen folgendermaßen bereitstellen:

context.Database.SqlQuery<myEntityType>(
    "mySpName @param1, @param2, @param3",
    new SqlParameter("param1", param1),
    new SqlParameter("param2", param2),
    new SqlParameter("param3", param3)
);
Devart
quelle
3
Wie würden Sie diese Methode mit nullbaren Typen arbeiten lassen? Ich habe dies mit nullbaren Dezimalstellen versucht, aber wenn die Dezimalstellen null sind, erhalte ich die Fehlermeldung, dass der Parameter fehlt. Die unten von @DanMork erwähnte Methode funktioniert jedoch.
Paul Johnson
2
DbNull.ValueLöst das Übergeben anstelle von Nullen das Problem?
Alireza
29
Sie können auch die \ @ p # -Syntax verwenden, um die Verwendung von SqlParameter wie in context.Database.SqlQuery <myEntityType ("mySpName \ @ p0, \ @ p1, \ @ p2", param1, param2, param3) zu vermeiden. Quelle: msdn.microsoft.com/en-US/data/jj592907 . (Hinweis: musste \ @ verwenden, um Benutzerbenachrichtigungen zu vermeiden, sollte ohne Backslash gelesen werden.)
Marco
3
Wenn Sie DateTime-Parameter verwenden, müssen Sie auch den Parametertyp angeben, nicht nur Name und Wert. Beispiel: dbContext.Database.SqlQuery <Invoice> ("spGetInvoices @dateFrom, @dateTo", neuer SqlParameter {ParameterName = "dateFrom", SqlDbType = SqlDbType.DateTime, Value = startDate}, new SqlPar " SqlDbType = SqlDbType.DateTime, Value = endDate}); Ein weiterer wichtiger Punkt ist die Einhaltung der Reihenfolge der Parameter.
Francisco Goldenstein
Können Sie bitte überprüfen, was ich falsch mache? Ich habe Ihre Richtlinien befolgt, aber überhaupt keine Auswirkungen. stackoverflow.com/questions/27926598/…
Toxic
129

Sie können auch den Parameter "sql" als Formatbezeichner verwenden:

context.Database.SqlQuery<MyEntityType>("mySpName @param1 = {0}", param1)
Dan Mork
quelle
Musste dies abstimmen. Obwohl es nicht als Antwort akzeptiert wurde, ist es eine viel einfacher zu schreibende Lösung als die als Antwort ausgewählte.
Nikkoli
10
Diese Syntax beschäftigt mich ein wenig. Wäre es anfällig für SQL-Injection? Ich würde annehmen, dass EF "EXEC mySpName @ Param1 =" ausführt und es möglich wäre, "x 'GO [bösartiges Skript]" zu senden und einige Probleme zu verursachen?
Tom Halladay
10
@TomHalladay kein Risiko einer SQL-Injection - Die Methode zitiert und maskiert die Parameter weiterhin basierend auf ihrem Typ, genau wie die @ style-Parameter. Für einen Zeichenfolgenparameter würden Sie also "SELECT * FROM Users WHERE email = {0}" ohne Anführungszeichen in Ihrer Anweisung verwenden.
Ross McNab
In meinem Fall haben wir viele optionale Parameter für SP und haben keine Aufrufe mit SqlParameters ausgeführt, aber dieses Format hat den Trick gemacht, musste nur 'EXEC' am Anfang hinzufügen. Vielen Dank.
Onur Topal
1
Diese Antwort ist nützlich, wenn Sie Parameter für einen Prozess mit optionalen Parametern angeben müssen. Beispiel, das nicht funktioniert: ProcName @optionalParam1 = @opVal1, @optionalParam2 = @opVal2 Beispiel, das funktioniert:ProcName @optionalParam1 = {0}, @optionalParam2 = {1}
Garrison Neely
72

Diese Lösung ist (nur) für SQL Server 2005

Ihr seid Lebensretter, aber wie @Dan Mork sagte, müsst ihr EXEC zum Mix hinzufügen. Was mich auslöste war:

  • 'EXEC' vor dem Proc-Namen
  • Kommas zwischen Params
  • Deaktivieren Sie '@' in den Param-Definitionen (nicht sicher, ob dieses Bit erforderlich ist).

::

context.Database.SqlQuery<EntityType>(
    "EXEC ProcName @param1, @param2", 
    new SqlParameter("param1", param1), 
    new SqlParameter("param2", param2)
);
Tom Halladay
quelle
21
+1. Keine der Antworten mit den höheren Stimmen enthält exec, aber ich kann bestätigen, dass ich eine Ausnahme erhalte, wenn ich sie weglasse.
Jordan Gray
Vielen Dank, ich habe einen Fehler erhalten, EXEC hinzugefügt und der Fehler ist verschwunden. Der seltsame Teil war, wenn ich context.Database.SqlQuery <EntityType> ("ProcName '" + param1 + "', '" + param2 + "'") tat; Es hat funktioniert, aber wenn ich Parameter hinzugefügt habe, hat es nicht funktioniert, bis ich das EXEC-Schlüsselwort hinzugefügt habe.
Solmead
2
Zu Ihrer Information: Ich benötige das execSchlüsselwort nicht. +1 für das Entfernen des @ in den Parametern, das bringt mich immer durcheinander.
Nathan Koop
+1, mir fehlte EXEC und ich bekam immer wieder SqlExceptions mit der Meldung: Falsche Syntax in der Nähe von 'procName'.
A. Murray
1
@Ziggler bist du auf 2005 oder neuer? Das EXEC-Schlüsselwort war hauptsächlich ein Problem für diejenigen von uns, die gegen 2005 gingen.
Tom Halladay
15
return context.Database.SqlQuery<myEntityType>("mySpName {0}, {1}, {2}",
new object[] { param1, param2, param3 });

//Oder

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

//Oder

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

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

//Oder

using(var context = new MyDataContext())
{  
return context.Database.SqlQuery<myEntityType>("mySpName {0}, {1}, {2}",
param1, param2, param3).ToList();
}
Thulasiram
quelle
Es funktioniert für mich für Assembly EntityFramework.dll, v4.4.0.0
Thulasiram
2
Wenn Sie using (var context = new MyDataContext ()) verwenden, ist .ToList () obligatorisch.
Thulasiram
Ich habe einige Zeit damit verbracht, herauszufinden, dass .ToList () obligatorisch ist, um die richtige Ergebnismenge zu erhalten.
Halim
8

Die meisten Antworten sind spröde, da sie von der Reihenfolge der SP-Parameter abhängen. Benennen Sie besser die Parameter des gespeicherten Prozesses und geben Sie diesen parametrisierte Werte.

Um beim Aufrufen Ihres SP benannte Parameter zu verwenden, ohne sich um die Reihenfolge der Parameter kümmern zu müssen

Verwenden von benannten SQL Server-Parametern mit ExecuteStoreQuery und ExecuteStoreCommand

Beschreibt den besten Ansatz. Besser als Dan Morks Antwort hier.

  • Verlässt sich nicht auf die Verkettung von Zeichenfolgen und nicht auf die Reihenfolge der im SP definierten Parameter.

Z.B:

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
quelle
Es scheint, dass "params" ein reserviertes Schlüsselwort ist, daher glaube ich nicht, dass Sie es so verwenden können. Ansonsten war dies eine hilfreiche Antwort für mich. Vielen Dank!
ooXei1sh
@ ooXei1sh - behoben, mit sqlParamsVariable
Don Cheadle
Sie können es mit @ voranstellen, um ein reserviertes Wort zu verwenden, aber Sie sollten
StingyJack
6
db.Database.SqlQuery<myEntityType>("exec GetNewSeqOfFoodServing @p0,@p1,@p2 ", foods_WEIGHT.NDB_No, HLP.CuntryID, HLP.ClientID).Single()

oder

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

oder

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)

oder

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

Ich benutze diese Methode:

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

Ich mag es, weil ich nur Guids und Datetimes einfüge und SqlQuery die gesamte Formatierung für mich durchführt.

Malcolm O'Hare
quelle
1

Die Antwort von @Tom Halladay ist richtig mit der Erwähnung, dass Sie auch nach Nullwerten suchen und DbNullable senden sollten, wenn die Parameter null sind, da Sie eine Ausnahme wie erhalten würden

Die parametrisierte Abfrage '...' erwartet den Parameter '@parameterName', der nicht angegeben wurde.

So etwas hat mir geholfen

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

(Gutschrift für die Methode geht an https://stackoverflow.com/users/284240/tim-schmelter )

Dann benutze es wie folgt:

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

oder eine andere Lösung, einfacher, aber nicht generisch wäre:

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

Ich hatte die gleiche Fehlermeldung, als ich mit dem Aufrufen einer gespeicherten Prozedur arbeitete, die zwei Eingabeparameter verwendet und 3 Werte mit der SELECT-Anweisung zurückgibt, und das Problem wie unten in EF Code First Approach gelöst

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

UPDATE : Es sieht so aus, als würde bei SQL SERVER 2005 das fehlende EXEC-Schlüsselwort ein Problem verursachen. Damit es mit allen SQL SERVER-Versionen funktioniert, habe ich meine Antwort aktualisiert und EXEC in der folgenden Zeile hinzugefügt

 List<Sample> x = this.Database.SqlQuery<Sample>(" EXEC usp_NextNumberBOGetMulti @TableName, @FieldName", param).ToList();
Ziggler
quelle
Bitte siehe untenstehenden Link. Es ist nicht erforderlich, exec msdn.microsoft.com/en-us/data/jj592907.aspx
Ziggler
0

Ich habe meine mit EF 6.x so gemacht:

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


            }

Verdoppeln Sie nicht den SQL-Parameter. Einige Leute werden verbrannt, wenn sie dies mit ihrer Variablen tun

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