Как использовать DbContext.Database.SqlQuery <TElement> (sql, params) с хранимой процедурой? EF Code First 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, как в context.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 = startDate}, новый Parameter_name_Semeter_name_Source_Mar SqlDbType = SqlDbType.DateTime, Value = endDate}); Еще одной важной вещью является соблюдение порядка параметров.
Франсиско Гольдштейн

не могли бы вы проверить, что я делаю неправильно, я следую вашим указаниям, но никакого эффекта вообще нет stackoverflow.com/questions/27926598/…
Toxic

129

Также вы можете использовать параметр "sql" в качестве спецификатора формата:

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

Пришлось проголосовать за это. Хотя он не был принят в качестве ответа, гораздо проще написать решение, чем выбранное в качестве ответа.
Никколи

10
Этот синтаксис касается меня немного. Будет ли это восприимчивым к инъекции SQL? Я хотел бы предположить, что EF работает «EXEC mySpName @ Param1 =», и можно было бы отправить «x 'GO [вредоносный скрипт]» и вызвать некоторые проблемы?
Том Halladay

10
@TomHalladay - нет риска внедрения SQL-кода - метод все равно будет заключать в кавычки и экранировать параметры, основанные на их типе, так же, как параметры в стиле @ Поэтому для строкового параметра вы должны использовать «SELECT * FROM Users WHERE email = {0}» без кавычек в вашем утверждении.
Росс Макнаб

в моем случае у нас есть много необязательных параметров для SP, и не работают вызовы с SqlParameters, но этот формат делает свое дело, просто нужно было добавить «EXEC» в начале. Спасибо.
Онур Топал

1
Этот ответ полезен, если вам нужно указать параметры для процесса с необязательными параметрами. Пример, который не работает: ProcName @optionalParam1 = @opVal1, @optionalParam2 = @opVal2 Пример, который работает:ProcName @optionalParam1 = {0}, @optionalParam2 = {1}
Garrison Neely

72

Это решение (только) для SQL Server 2005

Вы, ребята, спасатели, но, как сказал @Dan Mork, вам нужно добавить EXEC в смесь. Что меня сбило с толку:

  • «EXEC» перед именем Proc
  • Запятые между парам
  • Отключение «@» в определениях параметров (не уверен, что этот бит требуется).

:

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

21
+1. Ни один из ответов с более высоким рейтингом не включает exec, но я могу подтвердить, что получаю исключение, если опущу его.
Джордан Грей

Спасибо, я получил ошибку, добавил EXEC и ошибка исчезла. Странная часть была, если бы я сделал context.Database.SqlQuery <EntityType> ("ProcName" "+ param1 +" ',' "+ param2 +" '"); это работало, но если я добавил параметры, это не работало, пока я не добавил ключевое слово EXEC.
Solmead

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

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

1
@ Зигглер, ты на 2005 или новее? EXEC ключевое слово в основном было проблемой для тех из нас , идя против 2005
Tom Халладей

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. Лучше назвать параметры хранимой процедуры и дать им параметризованные значения.

Для того, чтобы использовать именованные параметры при вызове вашего SP, не беспокоясь о порядке параметров

Использование именованных параметров 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)

Кажется, «params» - зарезервированное ключевое слово, поэтому я не думаю, что вы можете использовать его таким образом. В противном случае это был полезный ответ для меня. Спасибо!
ooXei1sh

@ ooXei1sh - исправлено с использованием sqlParamsпеременной
Дон Чидл

вы можете использовать префикс @, чтобы использовать зарезервированное слово, но на самом деле это не так
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 и Datetimes, а SqlQuery выполняет все форматирование для меня.


1

@Tom Halladay ответ правильный, с упоминанием, что вы shopuld также проверяете на нулевые значения и отправляете 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 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; }
}

ОБНОВЛЕНИЕ : похоже, что в 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();


            }

Не удваивайте sqlparameter, некоторые люди обжигаются, делая это со своей переменной

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.