RSS

Linq to SQL – Conceitos Avançados (Parte 3) – Trabalhando com Stored Procedures complexas

05 dez

 

Nesta terceira parte da série sobre Linq to SQL demonstrarei como trabalhar com Stored Procedures complexas.

Como esta é a última parte da série onde falarei sobre Stored Procedures, é muito interessante a leitura do artigo anterior onde falo sobre como trabalhar com procedures simples, veja aqui o artigo anterior.
Para este artigo vamos trabalhar com uma procedure bem mais complexa que a do artigo anterior, onde a mesma utilizará parâmetros de entrada, parâmetros de saída, tipos de retorno dinâmico e código de retorno.

 
Criando a Stored Procedure

Vamos criar a procedure ObtemGrupos, a mesma recebe um código de grupo de produtos, e retorna através do parâmetro de saída “@quantidadeProdutos” a quantidade de produtos que pertencem ao código do grupo solicitado. A mesma retorna ainda todas ou apenas algumas colunas da tabela de Grupos dependendo do
parâmetro “@todasColunas” e caso não exista um grupo com o código passado a mesma retorna 1, caso contrário retorna 0.

Vejamos como fica:


CREATE PROCEDURE ObtemGrupos
@codGrupo INT,
@todasColunas BIT,
@quantidadeProdutos INT OUTPUT
AS
 SET NOCOUNT ON

IF NOT EXISTS (SELECT 1 FROM tbProdutosGrupos WHERE codProdutoGrupo = @codGrupo)
 RETURN 1

IF @todasColunas = 1
 SELECT * FROM tbProdutosGrupos
 WHERE codProdutoGrupo = @codGrupo
 ELSE
 SELECT nome, sigla FROM tbProdutosGrupos
 WHERE codProdutoGrupo = @codGrupo

 SELECT @quantidadeProdutos = COUNT(*) FROM tbProdutos WHERE codProdutoGrupo = @codGrupo

RETURN 0

 

Mapeando a Procedure com Linq

Agora que criamos a procedure vamos mapear a mesma com nosso arquivo SerieLinq.dbml, para tanto basta arrastar/soltar dentro do design do arquivo DBML para que a mesma apareça na guia de “Methods Pane“. Conforme a imagem abaixo:

 

O seguinte código é gerado automaticamente pelo Linq mapeando nossa procedure:


[global::System.Data.Linq.Mapping.FunctionAttribute(Name="dbo.ObtemGrupos")]
 public ISingleResult<ObtemGruposResult> ObtemGrupos([global::System.Data.Linq.Mapping.ParameterAttribute(DbType="Int")] System.Nullable<int> codGrupo,
 [global::System.Data.Linq.Mapping.ParameterAttribute(DbType="Bit")] System.Nullable<bool> todasColunas,
 [global::System.Data.Linq.Mapping.ParameterAttribute(DbType="Int")] ref System.Nullable<int> quantidadeProdutos)
 {
 IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), codGrupo, todasColunas, quantidadeProdutos);
 quantidadeProdutos = ((System.Nullable<int>)(result.GetParameterValue(2)));
 return ((ISingleResult<ObtemGruposResult>)(result.ReturnValue));
 }

 

Observe que a propriedade quantidadeProdutos é passado como parâmetro, porém não é retornada após a execução do método. A mesma será recuperada apenas chamando result.GetParameterValue.
A classe ObtemGruposResult também foi criada, esta classe é o que irá representar o retorno da procedure contendo um único conjunto de resultados.

Porém, não é isto que queremos, pois desta forma é retornada somente um conjunto de resultados.

 
Customizando a classe de representação do DataContext

Primeiramente vamos criar um novo arquivo chamado SerieLinqDataContext.cs, este arquivo irá conter as definições para customização da classe de representação da nossa procedure.

Criaremos então uma classe para representação do retorno com apenas algumas colunas, ou seja, o retorno quando o parametro da procedure “todasColunas” for igual a 0.

Vejamos como fica nossa classe:


public class GrupoParcial
{
public string nome;
public string sigla;
}

Agora vamos criar uma segunda classe, porém esta será uma classe parcial da nossa classe SerieLinqDataContext, abaixo segue definição da classe:


public partial class SerieLinqDataContext
{
[Function(Name = "dbo.ObtemGrupos")]
[ResultType(typeof(GrupoParcial))]
[ResultType(typeof(tbProdutosGrupo))]
public IMultipleResults ObtemGruposCorrigido(

[global::System.Data.Linq.Mapping.ParameterAttribute(DbType = "Int")] System.Nullable<int> codGrupo,
 [global::System.Data.Linq.Mapping.ParameterAttribute(DbType = "Bit")] System.Nullable<bool> todasColunas,
 [global::System.Data.Linq.Mapping.ParameterAttribute(DbType = "Int")] ref System.Nullable<int>
quantidadeProdutos)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())),                codGrupo, todasColunas, quantidadeProdutos);

quantidadeProdutos = ((System.Nullable<int>)(result.GetParameterValue(2)));
 return ((IMultipleResults)(result.ReturnValue));
}
}

Como podemos ver, nós definimos um método ObtemGruposCorrigido para mapear os resultados para diferentes tipos, observe que definimos dois tipos de retorno.

NOTA: É possivel modificar diretamente no SerieLinq.designer.cs, porém sempre que ocorrer uma alteração suas correções serão perdidas.

 
Testando nossas implementações

Para testar nossas modificações vamos criar o seguinte método:


public static void ProcedureComplexa(int cod, bool todasColunas, SerieLinqDataContext _db)
 {
 int? quantidadeProdutos = 0;
 IMultipleResults result = _db.ObtemGruposCorrigido(cod, todasColunas, ref quantidadeProdutos);
 int codRetorno = (int)result.ReturnValue;

if (codRetorno == 0)
 {
 if (todasColunas == true)
 {
 tbProdutosGrupo pg = result.GetResult<tbProdutosGrupo>().FirstOrDefault();
 Console.WriteLine("Código: {0}", pg.codProdutoGrupo);
 Console.WriteLine("Nome: {0}", pg.nome);
 Console.WriteLine("Sigla: {0}", pg.sigla);
 Console.WriteLine("Data Cadastro: {0}", pg.dataCadastro.Value.ToShortDateString());
 }
 else
 {
 GrupoParcial gp = result.GetResult<GrupoParcial>().FirstOrDefault();
 Console.WriteLine("Nome: {0}", gp.nome);
 Console.WriteLine("Login: {0}", gp.sigla);
 }

Console.WriteLine("Quantidade de Produtos no Grupo: {0}", quantidadeProdutos);
 }
 else
 {
 Console.WriteLine("Não existe um grupo com o código({0}) informado - CÓD RETORNO {1}", cod, codRetorno);
 }
 Console.WriteLine("\n");
 }

E iremos realizar as seguintes chamadas:


//Todas as colunas do Grupo
Exemplos.ProcedureComplexa(6, true, _db);

//Apenas algumas as colunas do Grupo
Exemplos.ProcedureComplexa(7, false, _db);

//Grupo inválido
Exemplos.ProcedureComplexa(20, true, _db);

 

Como podemos ver abaixo, o retorno apropriado para cada tipo de solicitação a nossa procedure.
A primeira chamada retorna todas as colunas do grupo e exibe a quantidade de produtos, a segunda chamada retorna apenas algumas colunas (nome e silga) do grupo e a quantidade de produtos, já a terceira chamada exibe a mensagem, pois não existe um grupo com o código 20:

 

 

 

Anúncios
 
Deixe um comentário

Publicado por em 05/12/2011 em Linq

 

Tags: , , , ,

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

 
%d blogueiros gostam disto: