Stored Procedure x EDM
Como faco para executar uma procedure utilizando o entity framework. Quero executar uma procedure qualquer que me retorne algum valor. Como faco isso??
Nefrodata Ltda
Curtidas 0
Respostas
Luiz Maia
09/03/2009
Ola,
Para invocar um SP primeiramente vc deve definir seu modelo com o mapeamento da Stored Procedure.
SPs podem ser chamadas via codigo numa aplicação que usa Data Model.
Veja o seguinte exemplo/script abaixo para melhor entendimento:
1 - Por exemplo, temos 5 entidades: (Address, Contact, Product, SalesOrderDetail e SalesOrderHeader) na base AdventureWorks;
2 - Cria-se a SP:
USE [AdventureWorks]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID ( 'dbo.GetOrderDetails', 'P' ) IS NOT NULL
DROP PROCEDURE dbo.GetOrderDetails;
GO
CREATE PROCEDURE [dbo].[GetOrderDetails]
@SalesOrderHeaderId int
AS
SELECT SalesOrderID, SalesOrderDetailID, CarrierTrackingNumber,
OrderQty, ProductID, SpecialOfferID, UnitPrice, UnitPriceDiscount,
rowguid, ModifiedDate, LineTotal
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = @SalesOrderHeaderId;3 - Abra o arquivo SSDL (store squema definition language);4 - Adicione a seguinte função dentro do esquema de tags mas não dentro das tags do EntityContainer:( Nome da procedure com o parametro ) <Function Name="GetOrderDetails" Aggregate="false"
BuiltIn="false" NiladicFunction="false"
IsComposable="false"
ParameterTypeSemantics="AllowImplicitConversion"
Schema="dbo">
<Parameter Name="SalesOrderHeaderId" Type="int" Mode="In" />
</Function> 5 - Implementar o CSDL (conceptual squema definition language); 6 - Adicione a função FUNCTION IMPORT dentro da tag de EntityContainer do arquivo CSDL; 7 - Implementar o MSL (mapping specification language); 8 - Adicione a seguinte sintaxe de EntityContainerMapping: <FunctionImportMapping FunctionImportName="GetOrderDetails"
FunctionName="AdventureWorksModel.Store.GetOrderDetails"/> 9 - Recompile a aplicação e pronto. Aguardo seu retorno com a resposta quanto ao seu entendimento do script acima.Caso tenha ficado alguma dúvida, por favor me contate novamente. AbraçosAttLuiz Maia
GOSTEI 0
Luiz Maia
09/03/2009
Ola Pessoal,
Conseguiram implementar as SPs no EDM?
Estou aguardando qualquer dúvida, ok?
Abraços
Att
Luiz Maia
GOSTEI 0
Nefrodata Ltda
09/03/2009
Olá Luiz tudo bem? Entendi o exemplo que vc mandou sim, para mapear uma procedure que por exemplo calcula algo e me retorna o valor calculado eu faria o mesmo tipo de implementação?
Aguardo resposta o mais breve possivel
Atenciosamente
Julio
Nefrodata Sistemas
GOSTEI 0
Luiz Maia
09/03/2009
Sim Júlio.
Mesma coisa, só vai ter que alterar sus procedure quanto ao campo que quer que seja feito o calculo.
Tipo assim, em sua intrução SQL:
select sum(campoA) as totalCampoA from tabelaA where .....
Caso necessite de um exemplo, me avise que providencio pra vc.
Abraços
Att
Luiz Maia
GOSTEI 0
Nefrodata Ltda
09/03/2009
Luiz deixa eu te explicar melhor quero fazer uma procedure como esta abaixo:
create procedure CalculaValor(@valor int, @resultado int out)
as
begin
declare @temp = @valor * 2 / 10
set @resultado= @temp
end
Agora quero executar essa procedure pelo EDM o mapeamento e a chamada é feita do mesmo jeito??
Porque no modelo que me mandou estou fazendo uma select essa procedure não retorna set de dados?
Aguardo resposta
GOSTEI 0
Luiz Maia
09/03/2009
Sim, vai usar a mesma coisa. so vai ter que definir um ResultColunmBiding para retornar o resultado com um valor.
Veja a img abaixo, apos um insert, é retornado o novo ID inserrido na base:
Para a execução via código, segue exemplo:
static void createTestEntities()
{
Product product = new Product();
product.name = "My Product";
product.price = 100;
context1.AddToProduct(product);
Order order = new Order();
order.Product_1 = product;
order.quantity = 5;
context1.AddToOrder(order);
context1.SaveChanges();
Console.Out.WriteLine("Created a product with id " + product.id);
Console.Out.WriteLine("Created an order with id " + order.id);
}
Abraços
E aguardo retorno.
Att
Luiz Maia
GOSTEI 0
Nefrodata Ltda
09/03/2009
Luiz então eu só consigo executar uma procedure com o EDM se eu associa-la as funções insert, update ou delete?? Eu não consigo executar uma procedure independente??? Queria executar uma procedure em um momento especifico, não apos uma inclusão, edição ou exclusão.
Atenciosamente
Julio
Nefrodata sistemas
GOSTEI 0
Luiz Maia
09/03/2009
A adição de SP no EDM so pode ser feita usando o SSDL. Quando a SP interage com outras tabelas ou entidades ou objetos de outra classe, o EDM não consegue mapear automaticamente no CSDL. Se vc quer criar um metodo que mapeia a SP, vc so consegue fazer isto editando o arquivo XML no .edmx.
Para criar um metodo que retorna dados, vc primeiro escolhe a SP.
Vou mostrar os passos para tal procedimento.
Vamos adicionar um metodo GetTenMostExpensiveProducts, que retorna os 10 primeiros produtos mais caros na qyuery, ou seja, mais ou menos o que vc quer, uma query com calculos. O resultado sera retornado no objeto Produto.
Quando o SSDL ja reconhecer a SP como uma Função, o proximo passo é adicionar o metodo no CSDL. Isto pode ser feito adicionando o elemento FunctionIMport como um filho da tag EntityContainer, veja abaixo:
<FunctionImport Name="GetTenMostExpensiveProducts"
EntitySet="Products"
ReturnType="Collection(Self.Products)">
</FunctionImport>
The Name attribute indicates the name of the method on the entity container. The EntitySet attribute indicates the EntitySet, and the ReturnType attribute refers to the EntityType that will be returned (or the collection of EntityTypes in this case).O atributo NAME indica o nome do metodo na EntityContainer.O atributo EntitySet indica o proprio entityset.O ReturnType sua coleção retornada. AbraçosLuiz Maia
GOSTEI 0
Nefrodata Ltda
09/03/2009
Fábio vou tentar te explicar com mais detalhes o que preciso.
Eu não quero executar um procedure que me retorne dados como um set de dados, eu quero executar uma procedure independente uma procedure quer fará um cálculo e me retornará um único valor não uma lista de valores, somente um valor isolado ou numa outra situação uma procedure que executará algo mas não retornará nada (seria uma procedure que incluira registros numa tabela atualizará registros em outra tabela) mas não retornará nada. E a execução dessa procedure não esta associada a nenhuma tabela, não é uma procedure para ser executada na inclusão, edição, exclusão ou seleção de registros.
As respostas que você me mandou todas estão associadas a uma tabela, numa resposta vc associou uma procedure para inclusão em outra na seleção. Tem como eu mapear uma procedure no EDM para executada em um momento qualquer??
Aguardo resposta
Abraços
Julio
Nefrodata Sistemas
GOSTEI 0
Nefrodata Ltda
09/03/2009
Fábio conseguir fazer algo aqui veja só:
Implementei essa procedure
create procedure [dbo].[Calculo]
@valor int,
@retorno int outpup
as
begin
set @retorno = @valor * 2
end
Depois fiz o mapeamento da procedure no meu EDM:
Veja que ele esta mapeado no meu EDM:
Agora minha dúvida é como eu executo essa procedure por linha de código??
Aguardo resposta
Abraços
GOSTEI 0
Luiz Maia
09/03/2009
Bom, primeiramente sou o Luiz, não o Fábio!!!!
E quanto a sua implentação, é justamente o que eu te disse abaixo, vc mapeia a SP como um objeto, daí não importa para o EDM o que ele faz e retorna. Te passei os comandos via linha do codigo para alterar no XML, mas já que conseguiu melhor ainda. Repare que é exatamente o que te falei pra fazer, usando o Fucntion Import Name, so que vc usou o Wizard, eu te passei via codigo.
Agora já que vc tem um OBJETO mapeado (sua sp), basta utilizar o codigo que ja te passei tb, segue modificado para o que precisa:
static void Teste()
{
Calculo calc = new Calculo();
calc.retorno = 0;
calc.valor = 100;
context1.AddToCalculo(calc);
Console.Out.WriteLine("Valor retornado " + calc.SeuReturnType); // no seu caso 200;
}
Aguardo retorno
Abraços
Att
Luiz Maia
GOSTEI 0
Nefrodata Ltda
09/03/2009
Luiz me desculpe pela troca dos nomes.
Apesar da stored procedure estar mapeada no meu EDM ela não esta na minha classe.
Então não consigo ver a classe Calculo ou CalcularValor.
O que pode ter acontecido para ela não esta na classe??
Julio
Nefrodata sistemas
GOSTEI 0
Luiz Maia
09/03/2009
Julio,
O mapeamento exige tres passos:
Definir a importação do função no SSDL:
<Function Name="CalcularValor" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<Parameter Name="Valor" Type="int" Mode="In" /> <Parameter Name="Retorno" Type="int" Mode="Out" />
</Function> Expor a definição da função no arquivo CSDL: <EntityContainer Name="Calculo">
<FunctionImport Name="CalcularValor" ReturnType="Collection(Self.Calculo)">
<Parameter Name="Valor" Mode="In" Type="Int32" /> <Parameter Name="Retorno" Mode="Out" Type="Int32" />
</FunctionImport>
</EntityContainer> Definir o mapeamento no arquivo MSL: <EntityContainerMapping StorageEntityContainer="StoreContainer" CdmEntityContainer="CustomerEntityContainer">
<FunctionImportMapping FunctionImportName="CalcularValor" FunctionName="StoreNamespace.CalcularValor" />
</EntityContainerMapping> Verifique se esta tudo ok. Luiz Maia
<Parameter Name="Valor" Type="int" Mode="In" /> <Parameter Name="Retorno" Type="int" Mode="Out" />
</Function> Expor a definição da função no arquivo CSDL: <EntityContainer Name="Calculo">
<FunctionImport Name="CalcularValor" ReturnType="Collection(Self.Calculo)">
<Parameter Name="Valor" Mode="In" Type="Int32" /> <Parameter Name="Retorno" Mode="Out" Type="Int32" />
</FunctionImport>
</EntityContainer> Definir o mapeamento no arquivo MSL: <EntityContainerMapping StorageEntityContainer="StoreContainer" CdmEntityContainer="CustomerEntityContainer">
<FunctionImportMapping FunctionImportName="CalcularValor" FunctionName="StoreNamespace.CalcularValor" />
</EntityContainerMapping> Verifique se esta tudo ok. Luiz Maia
GOSTEI 0
Nefrodata Ltda
09/03/2009
Meu caro amigo Luiz verifique o arquivo SSL, CSDL, MSL e esta tudo lá. Porém verifiquei o arquivo .cs do meu EDM e não tem nenhuma classe implementada no arquivo.
(tirado de meu arquivo xml)
SSDL
<Function Name="CalcularValor" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<Parameter Name="valor" Type="int" Mode="In" />
<Parameter Name="resultado" Type="int" Mode="InOut" />
</Function>
CSDL
<FunctionImport Name="CalcularValor" ReturnType="Collection(Int32)">
<Parameter Name="valor" Mode="In" Type="Int32" />
<Parameter Name="resultado" Mode="InOut" Type="Int32" /></FunctionImport>
MSL
<FunctionImportMapping FunctionImportName="CalcularValor" FunctionName="Model.Store.CalcularValor" />
Pelo que estou vendo acho que o problema não esta no arquivo XML esta no fato dele não implementar a classe no arquivo .cs. Será que pode ter sido por eu ter mapeado a procedure pelo wizard??
Julio
GOSTEI 0
Luiz Maia
09/03/2009
Pessoal,
Só agora entendi sua dúvida perfeitamente, mas não tenho boas notícias. Veja a reposta que encontrei no suporte do VS do pessoal da Microsoft, quando perguntei sobre um suposto Bug no EDM:
It's not so much a bug as it is the lack of a feature. The Entity Framework just doesn't support stored procedures returning scalar values right now. I believe this is supposed to change in .NET 4.0. In the meantime, you can execute such a stored procedure by using the store connection, available via CreateDbCommand. Stored Procedures that return scalar data The LINQ to SQL Designer allows you to map stored procedures that return scalars. While the Entity Framework does have mapping support to specify stored procedures and functions that return scalars, automatic code-generation of methods that will allow you to use the mapping is currently missing. However, this is something that is being strongly considered for the next release of Entity Framework. In the meanwhile, the simple solution to this is to write extension methods on your context that will use the connection in order to execute the stored procedure/function and return results. The MS ADO.NET Team Ou seja, o framework 3.5 ainda não suporta o tipo de retorno de dados Scalars e Null, então vc terá que chamar sua SP via código, usando um CreateDBCommand. Usando o using System.Data.Common; Usando o Linq, com um arquivo .dbml (TesteDataContext no caso abaixo); var data = new TesteDataContext();
data.ExecuteCommand("CalcularValor"); Pode usar tb uma classe que ja faz o ExecuteCommand pra vc: public static int ExecuteCommand(this ObjectContext objectContext,
string command) {
DbConnection connection = ((EntityConnection)objectContext.Connection).StoreConnection;
bool opening = (connection.State == ConnectionState.Closed);
if (opening)
connection.Open();
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = command;
cmd.CommandType = CommandType.StoredProcedure;
try {
return cmd.ExecuteNonQuery();
}
finally {
if (opening && connection.State == ConnectionState.Open)
connection.Close();
}
} Daí é so chamar a SP e passar os parametros, mas via Linq, com EDM não tem como ainda. Abraços Luiz Maia
It's not so much a bug as it is the lack of a feature. The Entity Framework just doesn't support stored procedures returning scalar values right now. I believe this is supposed to change in .NET 4.0. In the meantime, you can execute such a stored procedure by using the store connection, available via CreateDbCommand. Stored Procedures that return scalar data The LINQ to SQL Designer allows you to map stored procedures that return scalars. While the Entity Framework does have mapping support to specify stored procedures and functions that return scalars, automatic code-generation of methods that will allow you to use the mapping is currently missing. However, this is something that is being strongly considered for the next release of Entity Framework. In the meanwhile, the simple solution to this is to write extension methods on your context that will use the connection in order to execute the stored procedure/function and return results. The MS ADO.NET Team Ou seja, o framework 3.5 ainda não suporta o tipo de retorno de dados Scalars e Null, então vc terá que chamar sua SP via código, usando um CreateDBCommand. Usando o using System.Data.Common; Usando o Linq, com um arquivo .dbml (TesteDataContext no caso abaixo); var data = new TesteDataContext();
data.ExecuteCommand("CalcularValor"); Pode usar tb uma classe que ja faz o ExecuteCommand pra vc: public static int ExecuteCommand(this ObjectContext objectContext,
string command) {
DbConnection connection = ((EntityConnection)objectContext.Connection).StoreConnection;
bool opening = (connection.State == ConnectionState.Closed);
if (opening)
connection.Open();
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = command;
cmd.CommandType = CommandType.StoredProcedure;
try {
return cmd.ExecuteNonQuery();
}
finally {
if (opening && connection.State == ConnectionState.Open)
connection.Close();
}
} Daí é so chamar a SP e passar os parametros, mas via Linq, com EDM não tem como ainda. Abraços Luiz Maia
GOSTEI 0
Nefrodata Ltda
09/03/2009
Ola Luiz valeu demais pela dica, então só nos resta esperar as proximas versões do EDM. Espero que sai logo.
Abraços
Júlio
Nefrodata Sistemas
PS: Voces tem previsão de quando sairá a proxima versão do EDM???
GOSTEI 0
Luiz Maia
09/03/2009
Na verdade, pelo que sei, virá junto com o Framework 4.0.
Esta versão ainda está em Beta, portanto não podemos ainda fazer julgamentos, tem que melhorar muito.
Na minha opnião, o Linq já era ruim, ai fizeram algo baseado no Linq.... Mas vamos esperar pra ver no que vai dar.
Pra ser um framework de mapeamento OR, vai ter que melhorar muito, ainda esta muito engessado, segurança é falha e nçao rola pra sistemas distribuidos ainda.
Se eu tivesse a oportunidade de usa-lo agora num novo sistema, eu esperaria um pouco, ate sair do Beta e instabilizar. Não rola de colocar em Produção algo instavel ainda.
Abraços
E ate mais....
Att
Luiz Maia
GOSTEI 0