Como usar DbContext.Database.SqlQuery <TElement> (sql, params) com o procedimento armazenado? Código EF Primeiro CTP5

250

Eu tenho um procedimento armazenado que possui três parâmetros e tenho tentado usar o seguinte para retornar os resultados:

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

No começo, tentei usar SqlParameterobjetos como parâmetros, mas isso não funcionou e joguei um SqlExceptioncom a seguinte mensagem:

O procedimento ou função 'mySpName' espera o parâmetro '@ param1', que não foi fornecido.

Então, minha pergunta é como você pode usar esse método com um procedimento armazenado que espera parâmetros?

Obrigado.

electricsheep
fonte
Qual versão do SQL Server você está usando? Estou tendo problemas com o código que funciona em 2008 no modo compat (90), mas quando o executo em 2005, ele falha com um erro de sintaxe.
Gats 02/02
4
@ Gats - Eu tive o mesmo problema com o SQL 2005. Adicione "EXEC" antes do nome do procedimento armazenado. Eu publiquei essas informações aqui para referência futura: stackoverflow.com/questions/6403930/…
Dan Mork

Respostas:

389

Você deve fornecer as instâncias SqlParameter da seguinte maneira:

context.Database.SqlQuery<myEntityType>(
    "mySpName @param1, @param2, @param3",
    new SqlParameter("param1", param1),
    new SqlParameter("param2", param2),
    new SqlParameter("param3", param3)
);
Devart
fonte
3
Como você faria esse método funcionar com tipos anuláveis? Eu tentei isso com decimais anuláveis, mas quando os decimais são nulos, recebo erros dizendo que o parâmetro está ausente. No entanto, o método abaixo mencionado pelos trabalhos de @DanMork encontra.
Paul Johnson
2
Passar em DbNull.Valuevez de nulos resolve o problema?
Alireza
29
Você também pode usar a sintaxe \ @ p # para evitar o uso de SqlParameter como no contexto.Database.SqlQuery <myEntityType ("mySpName \ @ p0, \ @ p1, \ @ p2", param1, param2, param3). Fonte: msdn.microsoft.com/en-US/data/jj592907 . (Nota: teve de uso \ @ para evitar notificações do usuário, deve ser lido sem a barra invertida.)
Marco
3
Se você estiver usando parâmetros DateTime, também precisará especificar o tipo de parâmetro, não apenas o nome e o valor. Por exemplo: dbContext.Database.SqlQuery <Invoice> ("spGetInvoices @dateFrom, @dateTo", novo SqlParameter {ParameterName = "dateFrom", SqlDbType = SqlDbType.DateTime, Valor = startDate}, novo SqlParameter {"ParameterName =" SqlDbType = SqlDbType.DateTime, Value = endDate}); Outra coisa importante é respeitar a ordem dos parâmetros.
Francisco Goldenstein
você pode gentilmente verificar o que estou fazendo de errado? Eu segui sua linha de orientação, mas não produz efeito algum stackoverflow.com/questions/27926598/…
Toxic
129

Além disso, você pode usar o parâmetro "sql" como um especificador de formato:

context.Database.SqlQuery<MyEntityType>("mySpName @param1 = {0}", param1)
Dan Mork
fonte
Teve que votar em cima disso. Embora não tenha sido aceita como resposta, é muito mais fácil escrever uma solução do que a selecionada como resposta.
Nikkoli 2/10/12
10
Essa sintaxe me preocupa um pouco. Seria suscetível à injeção de SQL? Eu diria que o EF está executando "EXEC mySpName @ Param1 =" e seria possível enviar "x 'GO [script malicioso]" e causar alguns problemas?
Tom Halladay
10
@ TomHalladay não há risco de injeção de SQL - o método ainda citará e escapará dos parâmetros com base em seu tipo, o mesmo que os parâmetros de estilo @. Portanto, para um parâmetro de string, você usaria "SELECT * FROM Users WHERE email = {0}" sem aspas em sua declaração.
Ross McNab
no meu caso, temos muitos parâmetros opcionais para o SP e não funcionamos com SqlParameters, mas esse formato é suficiente, basta adicionar 'EXEC' no início. Obrigado.
Página Inicial 'Onur Topal
1
Esta resposta é útil se você precisar especificar parâmetros para um proc com parâmetros opcionais. Exemplo que não funciona: ProcName @optionalParam1 = @opVal1, @optionalParam2 = @opVal2 Exemplo que funciona:ProcName @optionalParam1 = {0}, @optionalParam2 = {1}
Garrison Neely
72

Esta solução é (apenas) para o SQL Server 2005

Vocês são salvadores, mas como @Dan Mork disse, você precisa adicionar EXEC à mistura. O que estava me atrapalhando era:

  • 'EXEC' antes do nome do processo
  • Vírgulas entre Params
  • Cortando '@' nas Definições de Parâmetros (embora não seja necessário saber se esse bit é necessário).

:

context.Database.SqlQuery<EntityType>(
    "EXEC ProcName @param1, @param2", 
    new SqlParameter("param1", param1), 
    new SqlParameter("param2", param2)
);
Tom Halladay
fonte
21
+1. Nenhuma das respostas mais votadas inclui exec, mas posso confirmar que recebo uma exceção se a omitir.
Jordan Gray
Obrigado, eu estava recebendo um erro, adicionei EXEC e o erro desapareceu. A parte estranha foi se eu fiz context.Database.SqlQuery <EntityType> ("ProcName '" + param1 + "', '" + param2 + "'"); funcionou, mas se adicionei parâmetros, não funcionou até adicionar a palavra-chave EXEC.
Solmead
2
FYI: Não preciso da execpalavra - chave. +1 para a remoção do @ nos parâmetros, que sempre me atrapalha.
Nathan Koop 30/01
+1, estava faltando o EXEC e continuava recebendo SqlExceptions com a mensagem: Sintaxe incorreta perto de 'procName'.
A. Murray
1
@Ziggler você está em 2005 ou mais recente? A palavra-chave EXEC tem sido principalmente um problema para aqueles que estão indo contra 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
fonte
está trabalhando para mim para Assembly EntityFramework.dll, v4.4.0.0
Thulasiram 18/12
2
se você estiver usando (var context = new MyDataContext ()), então .ToList () é obrigatório.
Thulasiram 18/10/12
Passei uma boa quantidade de tempo para descobrir que .ToList () é obrigatório para obter o conjunto de resultados correto.
Halim
8

A maioria das respostas é frágil porque depende da ordem dos parâmetros do SP. Melhor nomear os parâmetros do Stored Proc e atribuir valores parametrizados a eles.

Para usar parâmetros nomeados ao chamar seu SP, sem se preocupar com a ordem dos parâmetros

Usando parâmetros nomeados do SQL Server com ExecuteStoreQuery e ExecuteStoreCommand

Descreve a melhor abordagem. Melhor do que a resposta de Dan Mork aqui.

  • Não depende de concatenação de strings e não depende da ordem dos parâmetros definidos no SP.

Por exemplo:

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
fonte
Parece que "params" é uma palavra-chave reservada, então não acho que você possa usá-la dessa maneira. Caso contrário, essa seria uma resposta útil para mim. Obrigado!
OoXei1sh 11/04
@ ooXei1sh - corrigido, usando a sqlParamsvariável #
Don Cheadle
você pode prefixo com @ para usar uma palavra reservada, mas você realmente não deveria
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
fonte
3

Eu uso este método:

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

Eu gosto porque simplesmente uso Guids e Datetime e o SqlQuery executa toda a formatação para mim.

Malcolm O'Hare
fonte
1

A resposta de @Tom Halladay está correta com a menção de que você também deve procurar valores nulos e enviar DbNullable se os parâmetros forem nulos, pois você obteria uma exceção como

A consulta parametrizada '...' espera o parâmetro '@parameterName', que não foi fornecido.

Algo assim me ajudou

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

(o crédito para o método vai para https://stackoverflow.com/users/284240/tim-schmelter )

Em seguida, use-o como:

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

ou outra solução, mais simples, mas não genérica, seria:

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

Recebi a mesma mensagem de erro quando estava trabalhando com a chamada de um procedimento armazenado que usa dois parâmetros de entrada e retorna 3 valores usando a instrução SELECT e resolvi o problema como abaixo na Primeira abordagem de código 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; }
}

ATUALIZAÇÃO : Parece que com a falta da palavra-chave EXEC do SQL SERVER 2005 está criando um problema. Então, para permitir que ele funcione com todas as versões do SQL SERVER, atualizei minha resposta e adicionei EXEC na linha abaixo

 List<Sample> x = this.Database.SqlQuery<Sample>(" EXEC usp_NextNumberBOGetMulti @TableName, @FieldName", param).ToList();
Ziggler
fonte
Por favor, veja o link abaixo. Não há necessidade de usar exec msdn.microsoft.com/en-us/data/jj592907.aspx
Ziggler
0

Eu fiz o meu com o EF 6.x assim:

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


            }

Não dobre o sqlparameter, algumas pessoas se queimam fazendo isso com suas variáveis

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