Як використовувати DbContext.Database.SqlQuery <TElement> (sql, params) із збереженою процедурою? Код EF Перший CTP5


250

У мене є збережена процедура, яка має три параметри, і я намагався використовувати наступні для повернення результатів:

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

Спочатку я спробував використати SqlParameterоб’єкти як парами, але це не спрацювало, і я кинув SqlExceptionтаке повідомлення:

Процедура або функція 'mySpName' очікує параметр '@ param1', який не був наданий.

Отже, моє питання полягає в тому, як ви можете використовувати цей метод із збереженою процедурою, яка очікує параметрів?

Дякую.


Яку версію SQL Server ви використовуєте? У мене виникають проблеми з кодом, який працює в 2008 році в режимі compat (90), але коли я запускаю його проти 2005 року, він не працює із синтаксичною помилкою.
Гетс

4
@Gats - У мене був той самий випуск з SQL 2005. Додайте "EXEC" перед збереженою назвою процедури. Я розмістив цю інформацію тут для подальшої довідки: stackoverflow.com/questions/6403930/…
Ден Морк

Відповіді:


389

Ви повинні надати екземпляри SqlParameter наступним чином:

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

3
Як би ви зробили цей метод роботи з змінними типами? Я спробував це з нульовими десятковими знаками, але коли децималі є нульовими, я отримую помилки, кажучи, що параметр відсутній. Однак знайдений нижче метод @DanMork працює.
Пол Джонсон

2
Чи DbNull.Valueвирішення проблеми замість нулів вирішує проблему?
Аліреза

29
Ви також можете використовувати синтаксис \ @ p #, щоб уникнути використання SqlParameter як у контексті.Database.SqlQuery <myEntityType ("mySpName \ @ p0, \ @ p1, \ @ p2", param1, param2, param3). Джерело: msdn.microsoft.com/en-US/data/jj592907 . (Примітка: довелося використовувати \ @, щоб уникнути сповіщень користувачів, слід читати без зворотної косої риси.)
Марко

3
Якщо ви використовуєте параметри DateTime, вам також потрібно вказати тип параметра, а не лише ім'я та значення. Наприклад: dbContext.Database.SqlQuery <Invoice> ("spGetInvoices @dateFrom, @dateTo", новий SqlParameter {ParameterName = "dateFrom", SqlDbType = SqlDbType.DateTime, Value = startDateTame, newq {DateDate}, Newq Paramete SqlDbType = SqlDbType.DateTime, значення = endDate}); Ще одна важлива річ - це дотримання порядку параметрів.
Франсіско Голденштейн

Ви можете люб’язно перевірити, що я роблю неправильно, я дотримуюся ваших рекомендацій, але ніякого ефекту немає на stackoverflow.com/questions/27926598/…
токсичний

129

Крім того, ви можете використовувати параметр "sql" як специфікатор формату:

context.Database.SqlQuery<MyEntityType>("mySpName @param1 = {0}", param1)

Довелося це проголосувати. Хоча це не було прийнято як відповідь, його рішення набагато простіше написати, ніж те, що було обрано як відповідь.
Нікколі

10
Цей синтаксис мене трохи стосується. Чи було б це сприйнятливим до ін'єкції SQL? Я б припустив, що EF працює "EXEC mySpName @ Param1 =", і можна було б надіслати "x 'GO [шкідливий сценарій]" і викликати деякі проблеми?
Том Халладей

10
@TomHalladay відсутній ризик ін'єкції SQL - метод все одно цитуватиме та виводитиме параметри на основі їх типу, такий же, як і параметри стилю @. Отже, для параметра рядка ви використовуєте "SELECT * FROM Users WHERE email = {0}" без лапок у вашому виписці.
Ross McNab

у моєму випадку у нас є безліч необов’язкових параметрів для SP і не працювали дзвінки з SqlParameters, але цей формат зробив свою справу, просто довелося додати "EXEC" на початку. Дякую.
Онур Топал

1
Ця відповідь корисна, якщо вам потрібно вказати параметри для програми з необов’язковими параметрами. Приклад, який не працює: ProcName @optionalParam1 = @opVal1, @optionalParam2 = @opVal2 Приклад, який працює:ProcName @optionalParam1 = {0}, @optionalParam2 = {1}
Гарнізон Нілі

72

Це рішення є (лише) для SQL Server 2005

Ви, хлопці, рятувальники, але, як сказав @Dan Mork, вам потрібно додати EXEC до суміші. Що мене спонукало:

  • "EXEC" перед назвою Proc
  • Коми між Парамами
  • Відсікання параметра "@" у визначеннях Param (хоча не впевнений, що біт потрібен).

:

context.Database.SqlQuery<EntityType>(
    "EXEC ProcName @param1, @param2", 
    new SqlParameter("param1", param1), 
    new SqlParameter("param2", param2)
);

21
+1. Жодна з вищих проголосованих відповідей не включає exec, але я можу підтвердити, що я отримую виняток, якщо пропускаю його.
Джордан Грей

Дякую, я отримував помилку, додав EXEC і помилки вже немає. Дивна частина була, якщо я зробив контекст.Database.SqlQuery <EntityType> ("ProcName" "+ param1 +" ',' "+ param2 +" '"); він працював, але якщо я додав параметри, він не працював, поки я не додав ключове слово EXEC.
Solmead

2
FYI: я не потребую execключового слова. +1 для видалення знака @ у парамах, що завжди мене заплутало.
Натан Куп

+1, мені не вистачало EXEC, і я постійно отримував SqlExceptions з повідомленням: Неправильний синтаксис біля "procName".
А. Мюррей

1
@ Ziggler ти 2005 року чи новіше? Ключове слово EXEC в основному було проблемою для тих, хто з нас пішов проти 2005 року.
Том Халладей,

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

// Або

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

// Або

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

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

// Або

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

це працює для мене для Assembly EntityFramework.dll, v4.4.0.0
Thulasiram

2
якщо ви використовуєте (var context = new MyDataContext ()), тоді .ToList () є обов'язковим.
Туласірам

Я витратив трохи гарної кількості часу, щоб виявити, що .ToList () є обов'язковим, щоб отримати правильний результат.
Халім

8

Більшість відповідей крихкі, оскільки вони покладаються на порядок параметрів SP. Краще назвати параметри Stored Proc і надати їм параметризовані значення.

Для того, щоб використовувати іменовані парами під час виклику вашого ІП, не турбуючись про порядок параметрів

Використання іменованих параметрів SQL Server із ExecuteStoreQuery та ExecuteStoreCommand

Описує найкращий підхід. Тут краще, ніж відповідь Дена Морка.

  • Не покладається на об'єднувальні рядки і не покладається на порядок параметрів, визначених у SP.

Наприклад:

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)

Здається, "парами" - це зарезервоване ключове слово, тому я не думаю, що ви можете його використовувати так. Інакше це була корисна відповідь для мене. Дякую!
ooXei1sh

@ ooXei1sh - виправлено, використовуючи sqlParamsзмінну
Don Cheadle

ви можете встановити префікс за допомогою @ для використання зарезервованого слова, але вам справді не слід
StingyJack

6
db.Database.SqlQuery<myEntityType>("exec GetNewSeqOfFoodServing @p0,@p1,@p2 ", foods_WEIGHT.NDB_No, HLP.CuntryID, HLP.ClientID).Single()

або

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

або

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)

або

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

3

Я використовую цей метод:

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

Мені це подобається, тому що я просто впадаю в Guids and Datetimes, а SqlQuery виконує все форматування для мене.


1

@Tom Halladay відповідь правильна, згадуючи, що ви також можете перевірити наявність нульових значень і надіслати DbNullable, якщо парами є нульовими, оскільки ви отримаєте виняток, як

Параметризований запит '...' очікує параметр '@parameterName', який не був наданий.

Щось подібне мені допомогло

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

(кредит на метод посилається на https://stackoverflow.com/users/284240/tim-schmelter )

Потім використовуйте його так:

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

або інше рішення, більш просте, але не загальне, таке:

new SqlParameter("@parameterName", parameter??(object)DBNull.Value)

0

У мене було те саме повідомлення про помилку, коли я працював з викликом збереженої процедури, яка приймає два вхідні параметри і повертає 3 значення за допомогою оператора SELECT, і я вирішив проблему, як показано нижче, у першому підході до коду EF

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

ОНОВЛЕННЯ : Схоже, у SQL SERVER 2005 відсутнє ключове слово EXEC створює проблему. Отже, щоб дозволити йому працювати з усіма версіями SQL SERVER, я оновив свою відповідь і додав EXEC у нижньому рядку

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

Перегляньте посилання нижче. Там немає необхідності використовувати EXEC msdn.microsoft.com/en-us/data/jj592907.aspx
Ziggler

0

Я зробив шахту з EF 6.x так:

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


            }

Не подвоюйте на sqlпараметр, деякі люди згоряють, роблячи це своєю змінною

var Action = new SqlParameter("@Action", 1);  // Don't do this, as it is set below already.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.