Tipos Complexos com Code First

Olá pessoal, vamos dar continuidade sobre os posts do Code First, agora falando um pouco sobre Tipos Complexos (Complex Types).

1. Tipos Complexos

Tipos Complexos (Complex Types) é uma funcionalidade integrada do ADO.Net Entity Framework 4.1 que permite a composição de várias Classes de Domínio em uma única Tabela a ser persistida no banco de dadoss, por exemplo:, vejam a Figura 1:

Figura 1. Classes de Domínio

Conceito: Granularidade define o nível de detalhamento de uma aplicação, ou seja, Granularidade Grossa: indica que existirão poucas classes com uma grande quantidade de detalhes (Poucos grãos, porem grossos). Granularidade Fina: consiste em várias classes com pouco detalhamento (no nosso caso, propriedades).

O Diagrama acima mostra um alto nível de detalhamento no nosso conjunto de classes que compõem a entidade Cliente. Explodimos a classe Cliente em um conjunto de classes, agrupando o código que poderá se repetir em uma classe única e referenciando em uma propriedade, dessa forma nosso código se torna mais reutilizável e entendível.

Um detalhe importante é que a classe que irá compor um Complex Type não poderá ter identidade (por exemplo, TelefoneId). Segue abaixo na Listagem 1 o código que compõe nosso conjunto de classes.

using System;

namespace Entities
{
    public class Cliente
    {
        public int ClienteId { get; set; }
        public string Nome { get; set; }
        public Telefone TelefoneResidencial { get; set; }
        public Telefone TelefoneCelular { get; set; }
        public Telefone TelefoneTrabalho { get; set; }
        public Endereco Endereco { get; set; }
    }

    public class Telefone
    {
        public int CodigoArea { get; set; }
        public Int32 Numero { get; set; }
    }

    public class Endereco
    {
        public string Rua { get; set; }
        public int Numero { get; set; }
        public string Complemento { get; set; }
        public Int32 CEP { get; set; }
        public string Bairro { get; set; }
    }

}

Listagem 1. Código que compõe a Tipo Complexo Cliente

2.  Mapeando o Tipo Complexo

O mapeamento do Tipo Complexo é feito de forma similar a de qualquer outra classe que deverá ser persistida utilizando o Code First, Através do DbSet é informado a classe principal e definido seu nome, conforme mostra a Listagem 2.

using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace Entities
{
    public class MyContext:DbContext
    {
        public DbSet<Cliente> Clientes { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            base.OnModelCreating(modelBuilder);
        }
    }
}

3. Criando um Dado

Agora com o Tipo criado e  Mapeado, vamos criar o primeiro cliente de forma ao Code First mapear nosso Conjunto de Classes e criar os objetos necessários a serem persistidos no banco de dados (para maiores detalhes, ver postes anteriores).  Para isso vamos instanciar nossa classe de contexto, criar um novo banco de dados, depois criar um novo cliente e então salvar os dados. Segue código na Listagem 3.

using System;
using Entities;
namespace CodeFirstConsole
{
    class Program
    {
        static void Main(string[] args)
        {

            using (Entities.MyContext MyContext = new MyContext())
            {
                if (MyContext.Database.Exists())
                {
                    Console.WriteLine("BD existente, deletando...");
                    MyContext.Database.Delete();
                }
                Console.WriteLine("Criando BD");
                MyContext.Database.Create();

                MyContext.Clientes.Add(new Cliente()
                {
                    Nome = "Olavo Oliveira Neto",
                    Endereco = new Endereco() { Rua = "XXX", Bairro = "YYY", CEP = 00000000, Numero = 1 },
                    TelefoneCelular = new Telefone() { CodigoArea = 85, Numero = 123456789 },
                    TelefoneResidencial = new Telefone() { CodigoArea = 85, Numero = 987654321 },
                    TelefoneTrabalho = new Telefone() { CodigoArea = 85, Numero = 789654123 }
                });

                MyContext.SaveChanges();
                Console.WriteLine("Processo Terminando, Pressione qualquer tecla para fechar.");
                Console.Read();

            }
        }
    }
}

Após a execução do código, poderemos ver no banco de dados que foi criado uma tabela com a seguinte disposição:

Figura 2. Classe Cliente

Na Figura 2, é possivel ver que foi criado uma única tabela, que consiste em todas as classes do nosso Tipo Complexo Cliente.

Bom, isso foi só um pouco mais sobre Code First e Tipos Complexos.

Publicado em Code First, Entity Framework, LINQ | Deixe um comentário

Entity Framework Code First

Olá pessoal, quanto tempo em?

Voltando aqui para falar um pouco sobre o Code First, uma nova feature do Microsoft ADO.Net Entity Framework 4.1, que poderá ser baixado do site: http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=8363 ou de outras formas que eu irei falar mais para frente 🙂

Code First, O que é ?

  1. A tecnologia de Mapeamento Objeto-Relacional(O/RM) da Microsoft fornece uma série de abordagens para o desenvolvimento de aplicações, ou seja, resolver um dos questionamentos do que virá primeiro, o banco de dados ou a aplicação. Para isto o Entity Framework nós fornece três possibilidades:
  • Database First: A partir de um banco de dados já existente, o Entity Framework irá realizar o mapeamento das tabelas e criar o as entidades correspondentes.
  • Model First: Introduzido na Framework 4.0, o Model First permite que seja criado primeiramente o mapeamento relacional da aplicação através da ferramenta gráfica do Visual Studio para que então seja criado o banco de dados da aplicação.
  • Code First: Eis nosso foco, a nova feature da Entity Framework que permite que através das classes da aplicação que representam as entidades de negócio seja possível manipular dados sem que um banco de dados já exista. Ou seja, através de uma aplicação nova ou já existente que contem uma série de classes POCO (Plain Old CLR Objects, leia mais em: http://blogs.msdn.com/b/efdesign/archive/2008/06/24/initial-poco-design-1-pager.aspx) é possível deixar para a Framework o trabalho de criar o banco, tabelas e mapeamento baseado na estrutura de classes da sua aplicação, incrível não é
Exemplo:
Inicialmente, vamos criar uma aplicação simples, somente para demonstrar com instalar e utilizar o Code First integrado ao Entity Framework, Para instalar o Entity Framework 4.1 e referenciar o projeto, irei utilizar o gerenciador de pacote NuGet (http://nuget.codeplex.com/)
Para isso, vamos criar um um Projeto Web Vazio, com o nome de MyCodeFirst, conforme a Figura 1

Criando Projeto MyCodeFirst

Definindo as classes POCO

Agora com o projeto criado, vamos criar nossas entidades Poco, para esse primeiro exemplo, vamos criar um conjunto de classes que armazenem dados sobre carros, motos e caminhões. Para isto, teremos as classe Marca, Modelo, uma classe abstrata Veiculo e três classes que à herdam. Segue na Figura 2 o diagrama de classe da nossa aplicação:

Diagrama de Classe POCO

Segue abaixo na listagem 1, o código de criação das entidades:

namespace MyCodeFirst
{
    public class Marca
    {
        public int MarcaId { get; set; }
        public string Nome { get; set; }
    }
    public class Modelo
    {
        public int ModeloId { get; set; }
        public string Nome { get; set; }
        public Marca Marca { get; set; }
    }
   public class Carro
    {
        public int CarroId { get; set; }
        public Modelo Modelo { get; set; }
        public int AnoFabricacao { get; set; }
        public int AnoModelo { get; set; }
        public int QtdRodas { get; set; }
        public string Cor { get; set; }
    }
}

Referenciando o Entity Framework 4.1 

Nossas classes já estão criadas, agora vamos adicionar a referência do Entity Framework 4.1 ao nosso projeto, para isto, vamos clicar com o botão direito sobre o projeto e então clicar em Manage NuGet Packages, Conforme a Figura 3

Nova Opção para Adicionar Referências

Na tela do Manage NuGet Packages, espere ele terminar de listar todos os pacotes, procure por EFCodeFirst e então clique em Install. O download da biblioteca será feito e você deverá confirmar a licença de utilização. Este processo poderá demorar um pouco. Por fim, você verá que foi baixado e instalado em sua aplicação a referência ao Entity Framework 4.1.

Tela de Seleção do NuGet Package

Criando a Classe de Contexto

Para quem já trabalhou com Entity Framework, utilizando um arquivo de mapeamento, sabe que o contexto é a classe que, além de várias outras funções, é responsável por gerenciar as conexões com o banco de dados, servir de proxy para realização de consultas e alteração e inserção de dados fazendo a montagem das entidades, etc.

Para o Code First, a classe de contexto é a classe que e irá informar para a aplicação, o que será mapeado para o banco de dados, qual e como acessar este banco de dados e quais ações deverão ser feitas para realizar este mapeamento (inclusive criar o próprio banco de dados) .

Para criar-mos nossa classe de contexto, devemos criar uma nova classe que herde a classe DbContext que encontra-se no namespace System.Data.Entity.

Agora que criamos a classe de contexto vamos informar o que será mapeado, isto deverá ser feito criando uma propriedade do tipo DbSet e informando via parâmetro genérico as entidades que serão mapeada. Veja abaixo na Listagem 2 como a classe ficará:

using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace MyCodeFirst
{
    public class AutomoveisContext:DbContext
    {
        public DbSet<Carro> Carros { get; set; }
        public DbSet<Marca> Marcas { get; set; }
        public DbSet<Modelo> Modelos { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
            base.OnModelCreating(modelBuilder);
        }
    }
}
A sobreposição do método OnModelCreating é uma forma de poder controlar alguns detalhes que  envolvem a criação do modelo no banco de dados e do mapamento, por exemplo inibir a pluralização do nome das tabelas.
Definindo a String de Conexão
Agora que já temos a classe de contexto pronta, vamos informar a aplicação, como ela irá se comunicar com o banco de dados.
Este passo não é verdadeiramente necessário caso a instalação do Visual Studio tenha sido a default, instalando também o Microsoft SQL Server 2005/2008 Express em uma instância nomeada de SqlExpress. O CodeFirst irá detectar que nenhuma string de conexão foi informada e irá criar uma conexão ao seu banco de dados, criando um banco com o nome da classe de contexto.
Caso esse não seja o caso, ou você deseje controlar isso, basta adicionar ao web.config uma string de conexão com o mesmo nome da classe de contexto, conforme exemplo abaixo na Listagem 3:

<connectionStrings>
  <add name ="AutomoveisContext"
       connectionString="Data Source=.\sqlexpress;Initial Catalog=AutomoveisDB;Integrated Security=True;MultipleActiveResultSets=True"
       providerName="System.Data.sqlClient"/>
</connectionStrings>

Testando a Aplicação

Agora que já temos tudo pronto para executar nossa aplicação, vamos criar uma página web que irá alimentar os dados (neste caso, irei fazer isso diretamente no código, sem interface com o usuário). O legal do Code First é que até agora nós não fizemos nada com relação a banco de dados e mapeamento com EDMX, mas, já está tudo pronto para funcionar 🙂

Segue abaixo na Listagem 4 o código da página WebForm1.aspx:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace MyCodeFirst
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        AutomoveisContext automoveisDB = null;

        protected void Page_Load(object sender, EventArgs e)
        {
            automoveisDB = new AutomoveisContext();

            if (!IsPostBack)
            {
                this.criarBanco();
                this.registraMarcas();
                this.registraModelos();
                this.registraCarros();
            }
        }
        private void registraMarcas()
        {
            automoveisDB.Marcas.Add(new Marca() { Nome = "Chevrolet" });
            automoveisDB.Marcas.Add(new Marca() { Nome = "Honda" });
            automoveisDB.Marcas.Add(new Marca() { Nome = "Fiat" });
            automoveisDB.Marcas.Add(new Marca() { Nome = "Renault" });
            automoveisDB.SaveChanges();
        }

        private void registraModelos()
        {
            //Para Chevrolet
            Marca marca = automoveisDB.Marcas.Single(c => c.Nome.Equals("Chevrolet"));
            automoveisDB.Modelos.Add(new Modelo() { Marca = marca, Nome = "Camaro" });
            automoveisDB.Modelos.Add(new Modelo() { Marca = marca, Nome = "Agile" });
            //Para Honda
            marca = automoveisDB.Marcas.Single(c => c.Nome.Equals("Honda"));
            automoveisDB.Modelos.Add(new Modelo() { Marca = marca, Nome = "Fit" });
            automoveisDB.Modelos.Add(new Modelo() { Marca = marca, Nome = "Civiv" });
            //Para Honda
            marca = automoveisDB.Marcas.Single(c => c.Nome.Equals("Fiat"));
            automoveisDB.Modelos.Add(new Modelo() { Marca = marca, Nome = "500" });
            automoveisDB.Modelos.Add(new Modelo() { Marca = marca, Nome = "Punto" });
            //Para Renault
            marca = automoveisDB.Marcas.Single(c => c.Nome.Equals("Renault"));
            automoveisDB.Modelos.Add(new Modelo() { Marca = marca, Nome = "Clio" });
            automoveisDB.Modelos.Add(new Modelo() { Marca = marca, Nome = "Sandero" });
            automoveisDB.SaveChanges();
        }

        private void registraCarros()
        {
            //Camaro
            Modelo modelo = automoveisDB.Modelos.Single(c => c.Nome.Equals("Camaro"));
            automoveisDB.Carros.Add(new Carro()
                {
                    AnoFabricacao = 2011,
                    AnoModelo = 2011,
                    Cor = "Preto",
                    Modelo = modelo,
                });

            //Fit
            modelo = automoveisDB.Modelos.Single(c => c.Nome.Equals("Fit"));
            automoveisDB.Carros.Add(new Carro()
            {
                AnoFabricacao = 2010,
                AnoModelo = 2010,
                Cor = "Prata",
                Modelo = modelo,
            });

            //Sandero
            modelo = automoveisDB.Modelos.Single(c => c.Nome.Equals("Fit"));
            automoveisDB.Carros.Add(new Carro()
            {
                AnoFabricacao = 2010,
                AnoModelo = 2011,
                Cor = "Verde",
                Modelo = modelo,
            });
            automoveisDB.SaveChanges();
        }

        /// <summary>
        /// Método responsável por verificar se o banco de dados já existe
        /// se Sim = Deletar banco e Criá-lo novamente
        /// se Não, Criar banco
        /// </summary>
        private void criarBanco()
        {
            //Verifica
            if (automoveisDB.Database.Exists())
                automoveisDB.Database.Delete();
            automoveisDB.Database.Create();
        }
    }
}

Vejam que o código acima é simples e segue um pequeno fluxo:

1. Ele irá verificar se o banco de dados já está registrado no servidor informado. Caso sim, o code first irá deletar o banco e criá-lo novamente. Essa ação,  claro é bem prática para ambiente de desenvolvimento.
também é possivel utilizar o método CreateIfNotExists(); que como o próprio nome diz, ele somente irá criar o banco de dados caso ele não exista. Ambos os métodos retornam booleano informando se foi ou não possivel criar o banco.

2. Registra as marcas

3. Registra os modelos

4. Registra os carros

Depois da execução da página, é possivel observar no servidor de BD a criação do banco com o nome de AutomoveisDB e 4 tabelas. três delas nós que criamos e a outra (EdmMetadata) é uma tabela default do Code First responsável por armazenar um Hash do modelo existente. Esse Hash é utilizado para verificar se o modelo atual ainda é válido para banco criado, ou seja, se for criado um campo novo em alguma entidade, ou até mesmo uma nova entidade, os hashs não coincidirão e a utilização não será possivel.

Bom, espero que tenha dado pra entender um pouco do Code First, essa nova funcionalidade do Entity Framework que veio pra ajudar e muito os desenvolvedores.

Até a próxima

Publicado em C#, Code First, Entity Framework, LINQ | Deixe um comentário

Entity SQL

E ai pessoal, já faz um tempinho que eu não falo de LINQ então vou falar rapidamente sobre uma funcionalidade do Entity Framework que eu considero como essencial, mas que poucos desenvolvedores ainda conhecem. Estou falando do Entity SQL. Uma linguagem similar ao conhecido Transact-SQL (T-SQL) voltada para consultas às entidades mapeadas pelo Entity Framework direto no modelo conceitual.

Pode até parecer estranho que se possa utilizar uma linguagem SQL para consultas ao Entity Framework já que ele realiza o Mapeamento Objeto Relacional visando diminuir a impedância da aplicação. Mas entendamos que quando ele foi desenhado o LINQ já existia e foi apenas incorporado ao seu funcionando, mas que devido a sua gama de funções e facilidades de uso se tornou seu principal meio de consulta.

A principal vantagem em utilizar o Entity SQL é a possibilidade de realizar consultas que não são possíveis ou que se tornam muito complicadas com o LINQ to Entities, por exemplo:

  • Conversões dentro da query (o LINQ to Entities não suporta, por exemplo, que você utilize códigos como int.parse() dentro das consultas: o erro abaixo será mostrado em tempo de execução: LINQ to Entities does not recognize the method ‘Int32 Parse(System.String)’ method, and this method cannot be translated into a store expression.
  • Consultas dinâmicas que sua query seja montada durante a execução. Já vi códigos em LINQ gigantescos ou vários códigos LINQ um abaixo do outro dentro de vários IF-ELSE para conseguir fazer isto.
  • Melhor para fazer filtros e ordenações de forma dinâmica.

1 – Consultas Básicas

Para começar, entenda que o mapeamento ainda é necessário e que tudo acontece passando pelo nosso Objeto de Contexto.

As consultas poderão ser realizas utilizando o objeto de contexto, por exemplo:


//Listandos os produtos que tem valor maior que 1000 e menor igual a 3000
//Para este caso o uso do it. é obrigatorio, sem ele o campo não será encontrado
List<Produto> lstProdutos = ex.Produto.Where("it.valor > 1000 && it.valor <=3000").ToList();

Ou utilizando o ObjectQuery, informando qual a entidade que será consultada, por exemplo:


//Instanciando a classe ObjectQuery passamos como parâmetros a consulta e o objeto de contexto
//A Consulta se assemelha muito a um SQL tradicional
ObjectQuery<Produto> queryProdutos = new ObjectQuery<Produto>( "select VALUE p from Produto as p where p.valor >2000", ex);
lstProdutos = queryProdutos.ToList();

Sobre a consulta acima, notem que:

–      O VALUE faz o papel de * do nosso conhecido e temido SELECT * FROM. Caso se tente colocar o * mesmo assim, o seguinte erro será mostrado em tempo de execução: he query syntax is not valid. Near term ‘*’

–      Apesar de extensa, essa consulta não é obrigatória,ela pode ser substituída apenas pelo nome da entidade.

2 – Consultas dinâmicas, Ordenação e Filtros

O aspecto mais legal e que mais recomendo para a utilização do Entity SQL é justamente para aplicação de filtros e ordenadores. O LINQ mantém isso muito amarrado, não é possível por exemplo eu ordenar um consulta a minha entidades produtos alternando a ordenação entre o valor do produto ou o nome dele.  Para casos assim eu já vi a seguinte solução:


exampleEntities ex = new exampleEntities();
List<Produto> lstProdutos = null;
string ordenador = "";

 //Valor selecionado pelo usuário: CPF
ordenador = "Nome";
 switch (ordenador)
{
case "Id": lstProdutos = ex.Produto.OrderBy(c => c.Id).ToList();
break;
case "Nome": lstProdutos = ex.Produto.OrderBy(c => c.Nome).ToList();
break;
case "Valor": lstProdutos = ex.Produto.OrderBy(c => c.Valor).ToList();
break;
}

Vejam que não é uma solução muito elegante e bastante repetitiva, sem falar que qualquer nova coluna na tabela da entidade obrigará a mudanças no código.

Sendo assim, vamos refazer o exemplo utilizando o Entity SQL a nosso favor:


exampleEntities ex = new exampleEntities();
List<Produto> lstProdutos = null;
string ordenador = "";
 //Valor selecionado pelo usuário: Valor
ordenador = "Valor";

//Desta vez irei passar somente o nome da entidade no parâmetro de consulta
ObjectQuery<Produto> queryProdutos = new ObjectQuery<Produto>("Produto", ex);

 //chamamos o método OrdeyBy passando uma string que nada mais é do que
//a concatenação do it. e o nome da coluna desejada para ordenação
lstProdutos = queryProdutos.OrderBy(string.Format("it.{0}", ordenador)).ToList();

Ta bom por enquanto. Depois eu posto mais algumas coisas legais que se pode fazer com Entity SQL!!


Publicado em LINQ | 1 Comentário

Table Storage, Windows Azure

Continuando a série de posts sobre armazenamento no Windows Azure (Storage), vamos falar sobre Tables. Outra forma de persistência de dados existente no Windows Azure, que se baseia no armazenamento de objetos em grupos chamados de entidades.

1 – Windows Azure Tables

Uma coisa que é necessária deixar claro é que o Table Storage do Windows Azure trata-se de uma forma de persistência baseada em tabelas, porém, de forma não relacional como os bancos de dados tradicionais (Microsoft SQL Server, Oracle, e DB2) para este tipo de armazenamento o Azure disponibiliza o SQL Azure.

O Azure Table Storage é um serviço de armazenamento de dados não relacional que proporciona a persistência de entidades (estado dos objetos) dentro de tabelas que por sua vez ficam dentro de uma conta do Windows Azure. Estas tabelas funcionam como containers que armazenam um determinado tipo de entidade. Cada entidade contém suas devidas propriedades que funcionam como se fossem as colunas da tabela. Segue na Figura 1 uma imagem que ilustra a hierarquia do Table Storage.

Figura 1. Hierarquia Table Storage

Devido a sua natura não relacional, o Table Storage não é visto com muito bons olhos, principalmente por não conter uma forma de relacionar tabelas através de chave primaria e estrangeira.  Cada tabela não poderá exceder a 1MB e poderá conter no máximo 252 propriedades alem das três comentadas a seguir.

2 – Chave Primária

O fato do Table Storage não ser relacional não implica dizer que ele não contém uma chave primaria. Na verdade ele contém sim, mas em um conceito diferenciado de duas chaves que juntas compõem a identificação da entidade dentro da tabela. Essas chaves são definidas dentro de cada entidade através da herança da classe abstrata TableServiceEntity localizada no namespace Microsoft.WindowsAzure.StorageClient. Essa herança é que torna uma classe em uma entidade para ser persistida no Storage Services.

As propriedades herdadas são PartitionKey (string),RowKey (string) e TimeStamp (Datetime).  Sendo as duas primeiras a composição da identificação da entidade ou seja dentro da mesma tabela não poderá existir duas entidades com as duas propriedades iguais. Pode parecer estranho ao inicio para quem já está acostumado a trabalhar com dados relacionais, mas aqui caberá a arte do desenvolvedor criar uma forma de persistir os dados utilizando a composição de chaves ao seu favor.

  • PartitionKey: Primeira parte da Primary Key da entidade. Esta propriedade é de preenchimento obrigatório, e além de fazer parte da identificação ela também gerencia o particionamento dos dados dentro no balanceamento de carga dos servidores de armazenamento. Por exemplo:
    Digamos que sua empresa tenha uma tabela de produtos que utiliza o PartitionKey para diferenciar o tipo do produto. Sabe-se que no inicio da veiculação de uma propaganda sobre uma determinada oferta, a quantidade de consultas a um determinado tipo de produto aumenta. Através do PartitionKey é possível distribuir somente estes dados para outros nós do cluster de servidores para que através do balanceamento de carga não aconteça um sobrecarga de consultas em um único storage.
  • RowKey: Segunda parte da Primary Key, de preenchimento obrigatório.

Iniciando nosso exemplo prático, vamos definir uma entidade para cadastrar visitantes. Como já foi citado anteriormente, deverá ser importado para a classe o namespace Microsoft.WindowsAzure.StorageClient e herdada à classe abstrata TableServiceEntity, segue exemplo na Listagem 1.


using Microsoft.WindowsAzure.StorageClient;

namespace MyFirstAzure.Storage.Tabelas.Entidades
{
public class Visitante:TableServiceEntity
{

/// <summary>
/// Método Construtor Default
/// Se faz necessário para poder criar os objetos no retorno das consultas
/// </summary>
 public Visitante() { }

/// <summary>
/// Método Constrtutor para a criação de objetos a serem persistidos
/// </summary>
/// <param name="partitionKey">Chave de Particionamento</param>
/// <param name="rowKey">Identificador de Linha</param>
 public Visitante(string partitionKey, string rowKey): base(partitionKey, rowKey){ }

public string Nome { get; set; }

public string Cidade { get; set; }

public int Idade { get; set; }

public string Comentario { get; set; }
 }
}

Listagem 1. Definição de Entidaes

Vejam ainda na Listagem 1 que temos dois métodos construtores. O Default é obrigatório e sem ele não é possível retorno os objetos de através das consultas (caso tente ser feito, será mostrado um erro em tempo de execução). O Construtor com dois parâmetros serve para preencher os dados referentes à chave primaria da entidade.

3 – Gerenciando as Tabelas, Containers para as entidades

Como já foi dito, o Table Storage se divide em um container que armazena as Tabelas, este por sua vez armazenas as entidades. O controle das tabelas é feito através da classe nativa do Windows Azure, CloudTableClient. A Instanciação desta classe requer que seja informado a URL do Table Storage e das credenciais de acesso. No artigo passado (Blobs) existe uma demonstração de uma classe armazena estes dados e retorna-os em métodos estáticos (StorageControle).Segue na Listagem 2, o Código necessário para criar uma nova tabela.


public void criaTabela(string nome)
{
CloudTableClient myCloudTable = new CloudTableClient(StorageControle.TableURL, StorageControle.Credenciais);
myCloudTable.CreateTableIfNotExist(nome);
}

Listagem 2. CloudTableClient para gerenciar as tabelas

4 – Contexto, Persistindo os Dados

Depois de criada as tabelas, temos de persistir os dados (entidades) no banco de dados. Essa persistência é realizada através da herança da classe abstrata TableServiceContext. Está classe não contem nenhuma operação, pois todas são feitas na classe base.  Segue Código da classe na Listagem 3.

 

using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace MyFirstAzure.Storage.Tabelas
{
internal class TabelaContexto : TableServiceContext
{
public TabelaContexto(string path, StorageCredentials credenciais) : base(path, credenciais) { }
}

Listagem 3. Classe de Persistência dos dados

Criada a classe de persistência, vamos agora criar uma classe que irá operá-la. Essa classe irá receber as entidades a serem persistidas através de Generics e irá salva-los na base.  O método salvarTabela recebe a entidade a ser salva através dos parâmetros do método.

O método de  SalvarTabela é bem simples, ele primeiro certifica-se  que a tabela já existe (Aqui estou utilizando o nome da entidade para armazenar como tabela através do Type). Depois é chamado o AddObject da classe contexto passando como parâmetros o nome da tabela(criado na linha anterior) e a entidade a ser persistida e por fim ele salva todas as alterações no banco através do comando SaveChanges(). Segue  código na Listagem 4.

using System;
using System.Collections.Generic;
using System.Linq;using Microsoft.WindowsAzure.StorageClient;
namespace MyFirstAzure.Storage.Tabelas
{
public class TabelasControle<T> where T:TableServiceEntity
{
TabelaContexto myTabelaContexto = null;
CloudTableClient myCloudTable = null;
public TabelasControle()
{
myTabelaContexto = new TabelaContexto(StorageControle.TableURL, StorageControle.Credenciais);
myCloudTable = new CloudTableClient(StorageControle.TableURL, StorageControle.Credenciais);
}
public void criaTabela(string nome)
{
CloudTableClient myCloudTable = new CloudTableClient(StorageControle.TableURL, StorageControle.Credenciais);
myCloudTable.CreateTableIfNotExist(nome);
}
public void salvarTabela(TableServiceEntity entidade)
{
try
{
this.criaTabela(entidade.GetType().Name);
//Adiciona o objeto a tabela criada
myTabelaContexto.AddObject(entidade.GetType().Name, entidade);
//Salva as alterações
myTabelaContexto.SaveChanges();
}
catch (Exception ex) { throw ex;}}}}

Listagem 4. Classe de Controle de Persistência das Tabelas

Vamos também adicionar um método a nossa classe de Controle que dado uma tabela e seu PartitionKey, ele irá retornar a quantidade de registros salvos. Notem que a consulta é feita utilizando LINQ to Entities através do método CreateQuery. Segue código na Listagem 5.


public int qtdRegistros(string nomeTabela, string partitionKey)
{
this.criaTabela(nomeTabela);

var query = from c in myTabelaContexto.CreateQuery<T>(nomeTabela)
where c.PartitionKey.Equals(partitionKey)
select c;
return query.ToList().Count;
}

Listagem 5. Código que dado um PartitionKey, retorna a quantidade de registros salvos

Para terminamos a implementação da nossa classe de controle, vamos criar mais dois métodos responsáveis por retornar os dados armazenados. Os métodos terão o mesmo nome (listar) diferenciando apenas as suas assinaturas. A estrutura dos dois é similar: Utilizando LINQ consultamos o banco de dados através da instanciação da classe contexto e utilizando o método CreateQuery e retornamos uma lista dos dados obtidos. Segue implementação na Listagem 6.

 

public List<T> listar(string nomeTabela)
{
     var query = from c in myTabelaContexto.CreateQuery<T>(nomeTabela)
                 select c;
     return query.ToList();
}

public List<T> listar(string nomeTabela, string partitionKey)
{
     var query = from c in myTabelaContexto.CreateQuery<T>(nomeTabela)
                 where c.PartitionKey.Equals(partitionKey)
                 select c;
     return query.ToList();
}

Listagem 6. Métodos de Consulta da classe Controle


Para facilitar o entendimento, segue abaixo na Figura 2 o diagrama de classe da aplicação. As classes recolhidas são nativas as bibliotecas do Windows Azure.

Figura 2. Diagrama de Classe das tabelas que utilizam o Table Service

5 – Salvando e Retornado os dados

Para finalizar, vamos salvar os dados utilizando a estrutura que foi montada neste artigo. Aqui estou abstraindo um pouco as boas práticas de programação para torna o entendimento um pouco mais fácil.

Para salvar uma entidade precisamos inicialmente instanciar a classe que à representa preenchendo os métodos construtores com os dados que irão compor a chave primária, aqui utilizamos o PartitionKey para armazenar o nome da pessoa e o RowKey para armazenar um número seqüencial, assim podemos facilmente capturar os dados de somente um visitante.

A classe de controle deverá ser iniciada informando o tipo, segue exemplo na Listagem 7.


TabelasControle<Visitante> tcVisitante = new TabelasControle<Visitante>();
Visitante visitante = new Visitante(txtNome.Text, (tcVisitante.qtdRegistros("Visitante", txtNome.Text)+1).ToString());
visitante.Nome = "Olavo Neto";
visitante.Idade = 25;
visitante.Cidade = "Fortaleza";
visitante.Comentario = "Este é um teste";
 tcVisitante.salvarTabela(visitante);

Listagem 7. Persistindo dados

Para retorna as entidades também deverá ser utilizada a classe de controle, informando o nome da tabela para retornar todos os dados ou informando o PartitionKey para realizar um filtro nos dados salvos.

—–

Bom pessoal é isto, apesar de um pouco longo, espero ter demonstrado como é fácil trabalhar com Table Storage para Windows Azure e ter demonstrado que apesar de certas diferenças entre as plataformas (encaradas como limitações) trabalhar com persistência de entidades na plataforma de computação das nuvens da Microsoft é bastante simples.

Quem quiser ver como ficou o exemplo implementado, temporariamente o link http://8667ebce1752445bb9ddaf86ff7e4b68.cloudapp.net/Visitantes.aspx da minha conta do Azure ainda está funcionando.

Publicado em Windows Azure | Deixe um comentário

Blobs com Windows Azure, armazenando e publicando arquivos

Olá a todos!

Iniciando uma série de posts sobre o Windows Azure, a plataforma de computação das nuvens da Microsoft. Vamos falar um pouco sobre armazenamento e publicação de arquivos de arquivos com Blobs.

Entenda que tudo que permeia a computação nas nuvens tem um nível de abstração muito grande, por exemplo: Na computação nas nuvens não temos o conceito de diretórios físicos nas máquinas, não podemos simplesmente dizer que o usuário poderá fazer o upload de uma foto para o diretório “c:\upload\fotos”. Da mesma maneira não podemos criar um diretório virtual e apontar links para arquivos físicos nas máquinas.

Pensando nisto a Microsoft incorporou o conceito de BLOB (Binary Large Object ou Grande Objetos Binário) a sua plataforma de computação nas nuvens a fim de resolver um problema de armazenamento de arquivos locais e ao mesmo tempo criar um meio fácil de disponibilizá-los.

1. Windows Azure Blobs

Blobs para Windows Azure basicamente consistem em arquivos binários ou textos que são armazenados dentro de containers. Estes arquivos podem conter tags (metadatas) que descrevem o arquivo ou adicionam informações a ele. Cada container pode conter um ou mais arquivos de qualquer tipo especificado e permissões especificas, podendo ser público (aonde todos consegue baixar os arquivos) ou privado (através de uma chave criada e com tempos de expiração). Os containers por sua vez ficam todos dentro da sua conta de storage do Windows Azure.  Todas as contas já vêm preparadas para trabalhar com três tipos de persistências de dados, Blobs, Filas e Tabelas (iremos tratar sobre eles posteriormente). A Figura 1 ilustra a hierarquia de armazenamento dos Blobs.

Figura 1. Windows Azure Blobs

Outro detalhe que tem de ser levado em consideração com relação aos Blobs é seu endereço de publicação.  Para todos os tipos de armazenamentos possíveis existe um endereço único que deverá ser respeitado em sua formação. Esta url endereça o acesso ao tipo de armazenamento que está sendo utilizando ao container e ao arquivo. Veja na Figura 2 que este endereço é divido em várias partes, sendo a primeira o nome da conta de armazenamento seguido do tipo de persistência escolhido e ao final temos o nome do container e o nome do arquivo.

Figura 2. Endereço de um Blob

A url da sua conta de storage poderá ser conseguida atraves do portal de gerenciamento do Windows Azure no endereço: http://windows.azure.com

2. Preparando a máquina para trabalhar

Por se tratar do primeiro artigo sobre o Windows Azure, irei falar aqui em como preparar sua máquina desenvolver para a plataforma nas nuvens.  Alguns requisitos ao primeiro momento podem parecer exagero, mas vejam que o Azure SDK é uma plataforma completa de desenvolvimento off-line, que permite aos desenvolvedores criarem e emularem o ambiente computacional das nuvens totalmente desconectado.

  • Windows Server 2008, Windows Vista ou W7
  • Visual Studio 2010 ou Visual Web Developer 2010 Express Edition
  • SQL Server 2005 ou 2008(podendo e preferencialmente o Express que acompanha o Visual Studio 2010 na instancia sqlexpress)
  • Internet Information Services 7.0 ou superior

O download poderá ser feito pelo endereço http://www.microsoft.com/windowsazure/getstarted/default.aspx clicando no botão Get Tools & SDK.

O processo é quase todo automático e sem muito segredo.

Um detalhe importante: ao iniciar o Visual Studio, é obrigatório que ele esteja em modo administrador, para isto basta clicar com o botão direito em cima do nome dele no menu é clica em Executar Como Administrador, confirmar no aviso de segurança e pronto, Conforme a Figura 3 demonstra.

Figura 3. Executando o Visual Studio como Administrador

Agora já estamos com a máquina pronta para trabalhar, vamos iniciar nossa pequena página Web hospedada nuvens.  Aqui é bom que tenhamos uma conta do Windows Azure para podermos testar, mas, caso não a tenha ainda (é possível conseguir uma conta gratuita de 30 dias no site do Windows Azure, mas esta oferta é por tempo limitado) é possível trabalhar em modo off-line em desenvolvimento e testar localmente.

Abrindo o Visual Studio (em modo administrador), criando um novo projeto você verá a opção WINDOWS AZURE PROJECT, de o nome do projeto e aperte o OK para criá-lo. Uma tela irá se abrir (Figura 4) selecione Web Role e clique em OK.

Figura 4. Criando um Web Role

3. Criando, Listando e Deletando Containers

Para começar e para deixar nosso código mais enxuto vamos criar uma classe que será responsável por informar para nossa aplicação qual a URL da conta de armazenamento e as credenciais de acesso. Está credencial trata-se de uma chave string ou array de bytes fornecida pelo site do Windows Azure.  Conforme a Listagem 1, veja que é um código bem simples e que você pode armazenar estes dados no arquivo de configuração ou aonde você achar mais adequado.

using Microsoft.WindowsAzure;

using System.Configuration;

namespace MyFirstAzure

{

    public class StorageControle

    {

        internal static string BlobURL

        {

            get { return "http://olavoazure.blob.core.windows.net"; }

        }

        private static StorageCredentials credenciais = null;

        internal static StorageCredentials Credenciais

        {

            get

            {

                if (credenciais == null)

                {

                    credenciais = new StorageCredentialsAccountAndKey("olavoazure", "ChaveDeAcesso");

                }

                return credenciais;

            }

        }

    }

}

Listagem 1. Gerenciando credenciais

Caso você queria trabalhar em modo off-line, segue na Listagem 2 como você deve recuperar as informações de url e credenciais de acesso ao storage local utilizando a classe CloudStorageAccount.

using Microsoft.WindowsAzure;

using System.Configuration;

namespace MyFirstAzure

{

    public class StorageControle

    {

        internal static string BlobURL

        {

            get { return CloudStorageAccount.DevelopmentStorageAccount.BlobEndpoint.AbsoluteUri; }

        }

         internal static string TableURL

        {

            get { return CloudStorageAccount.DevelopmentStorageAccount.TableEndpoint.AbsoluteUri; }

        }

        internal static string QueueURL

        {

            get { return CloudStorageAccount.DevelopmentStorageAccount.QueueEndpoint.AbsoluteUri; }

        }

        private static StorageCredentials credenciais = null;

        internal static StorageCredentials Credenciais

        {

            get

            {

                if (credenciais == null)

                {

                    credenciais = CloudStorageAccount.DevelopmentStorageAccount.Credentials;

                }

                return credenciais;

            }

        }  

    }

}


Listagem 2. Credenciais em modo de desenvolvimento local

Inicialmente, todas as classes da Storage API do Windows Azure estão no namespace Microsoft.WindowsAzure.StorageClient então se lembre de sempre adicionar esta referencia as suas classes.

A classe responsável por gerenciar os containers é a CloudBlobClient, por ela podemos criar containers, listar os existentes, gerenciar metadatas e apagá-los. A instanciação da classe exige que seja passado o endereço da conta Blob no storage e as credenciais de acesso.

Depois de instanciado, temos de pegar a referencia ao container (mesmo que ele não exista ainda) para isto utilizamos o método GetContainerReference passando como parâmetro o nome do container. Este método retorna um tipo CloudBlobContainer responsável por gerenciar os Blobs internos a ele. O método CreateIfNotExists() cria o container verificando se ele já existe, caso já exista nada será feito. 

Atenção: O nome do container não deve conter letras másculas nem espaço entre as palavras.

As permissões também serão criadas agora, para isto devemos utilizar a classe BlobContainerPermissions setando a propriedade PublicAccess para Container (queremos com isto que todos consigam baixar o que está dentro dos contêineres) e passadas ao container através do método SetPermissions. Segue na Listagem 3 o método completo de criação de containers.

public void criarContainer(string nome)
{
    CloudBlobClient blobClient = new CloudBlobClient(StorageControle.BlobURL, StorageControle.Credenciais);
    //Sempre letras minusculas
    CloudBlobContainer blobContainer = blobClient.GetContainerReference(nome.ToLower().Replace(" ",""));
    blobContainer.CreateIfNotExist();
    //Permissões
    BlobContainerPermissions containerPermissions = new BlobContainerPermissions();
    containerPermissions.PublicAccess = BlobContainerPublicAccessType.Container;
    blobContainer.SetPermissions(containerPermissions);
}

Listagem 3. Código para criar um container

Para listar os containers já criados, conforme já foi dito, utilizamos a classe CloudBlobClient chamando o método ListContainers() aqui podemos passar como parâmetro o prefixo do nome do container. Veja exemplo na Listagem 4

public List<CloudBlobContainer> listaContainers()

{

    CloudBlobClient blobCLient = new CloudBlobClient(StorageControle.BlobURL, StorageControle.Credenciais);

   return blobCLient.ListContainers().ToList();

}

Listagem 4. Listando containers

 A deleção de um container é feita diretamente do objeto CloudBlobContainer, a sua instanciação pode ser feita através do GetContainerReference() da classe CloudBlobClient() ou informando ao construtor da classe o endereço do container e as credenciais de acesso. Este endereço é a junção da url Blob storage com o nome do container, por exemplo: http://olavoazure.blob.core.windows.net/imagens.  O método responsável pela deleção do container é o Delete(). Segue exemplo na Listagem 5.

public void deletaContainer(string containerPath)

{

   CloudBlobContainer blobContainer = new CloudBlobContainer(containerPath, StorageControle.Credenciais);

   blobContainer.Delete();

}

Listagem 5. Deletando containers

4. Upload de Arquivos, Criando Blobs

Como já foi dito, um Blob representa um arquivo que será adicionado a um container e poderá conter metadados. A classe responsável por gerenciar o Blob, realizando o upload do arquivo e armazenado as tags, é a CloudBlob. O upload do arquivo poderá ser feito através do endereço do arquivo, de um array de bytes, por uma stream ou ,no caso de textos, através de uma string.

A criação do Blob é feita através da classe CloudBlobClient,  utilizando o método GetBlobReference() e passando como parâmetro o nome do Blob ou sua URL. A definição dos metadados é feito na propriedade Attributes.Metadata é obrigatório que este passo seja feito DEPOIS do upload do arquivo. Logo depois de definido os metadados, é necessários salva-los, isto é feito através do método SetMetadada().

A classe CloudBlob contém algumas propriedades referentes aos dados do arquivo como tamanho, tipo e data de gravação. A maioria destas propriedades é somente leitura mas ContentType deverá ser informada. Como no item acima, existe um método chamado SetProperties() para salvar as propriedades modificadas logo após o upload do arquivo.

Segue o método implementado na Listagem 6, note que temos dois parâmetros na assinatura do método,  o primeiro é do tipo HttpPostedFile tipo retornado pelo Controle FileUpload na propriedade PostedFile. O segundo parâmetro é o endereço completo do container.

public void adicionarArquivo(System.Web.HttpPostedFile arquivo, string container)

{

   CloudBlobContainer blobContainer = new CloudBlobContainer(container, StorageControle.Credenciais);

   blobContainer.CreateIfNotExist();

  //Cria o BLOB

  CloudBlob myBlob = blobContainer.GetBlobReference(arquivo.FileName);

  myBlob.UploadFromStream(arquivo.InputStream);

  //MetaData

  myBlob.Attributes.Metadata.Add("FileName", arquivo.FileName);

  myBlob.Attributes.Metadata.Add("FileExtension", arquivo.ContentType);

  myBlob.SetMetadata();

  //Propriedades

  myBlob.Properties.ContentType = arquivo.ContentType;

  myBlob.SetProperties();

}

Listagem 6. Criando o Blob e fazendo o upload do arquivo

5. Listando e Deletando Blobs

A Listagem dos Blobs parte da classe do container (CloudBlobContainer) através do método ListBlobs que como o nome diz, ele retorna uma lista de Blobs. Para o método de exemplo da Listagem 7, criamos um DataTable somente para exemplificar como se faz para retornar os dados internos do Blob. Um detalhe importante é que para recuperar as propriedades internas do Blobs, antes é necessário utilizar o método FetchAttributes().


public System.Data.DataTable listarBlobs(string containerPath)

{

    //Tabela para retorno dos campos do Blob

    System.Data.DataTable dtBlobs = new System.Data.DataTable("Blobs");

    dtBlobs.Columns.Add("Nome");

    dtBlobs.Columns.Add("Tipo");

    dtBlobs.Columns.Add("Url");

    CloudBlobContainer blobContainer = new CloudBlobContainer(containerPath);

    //Lista Blobs existentes no container

    foreach (CloudBlob blob in blobContainer.ListBlobs())

    {

        blob.FetchAttributes();//Comando para recuperar os atributos do Blob

        dtBlobs.Rows.Add(new object[]{

        blob.Metadata["FileName"].ToString(),//Nome do Arquivo

        blob.Properties.ContentType,//Tipo

        blob.Uri.AbsoluteUri//Url

        });

    }

    return dtBlobs;

 }

Listagem 7. Listando os Blobs

Para finalizar, vamos deletar um Blob, a deleção é feita diretamente na classe CloudBlob chamado o método DeleteIfExists(). A Instanciação do Blob pode ser feita diretamente passando como parâmetros a URL do Blob e as credenciais de segurança. Segue exemplo na Listagem 8:


public void deleta(string blobPath)

{

     CloudBlob blob = new CloudBlob(blobPath, StorageControle.Credenciais);

     blob.DeleteIfExists();

}

Listagem 8. Deletando o Blob

Por enquanto é isto, espero ter demonstrado um pouco da API do Windows Azure. Depois de muito esperar e tentar eu finalmente consegui uma conta gratuita do Azure, pena que ela só estará disponível até o dia 16/03/2011 mas  até lá estou tentando implementar algo diariamente para brincar e aprender. A url do meu site é http://8667ebce1752445bb9ddaf86ff7e4b68.cloudapp.net/ Para postar Blobs tem de botar senha(devido à limitação do espaço) mas o resto está livre para quem quiser brincar também.

Publicado em Windows Azure | Deixe um comentário

LINQ to Entities, Trabalhando com Stored Procedures

Trabalhar com Stored Procedures é sem dúvida uma boa ideia quando se trabalha com banco de dados.  Encapsular funções de forma simples, diminuir o trafego na rede e diminuir a repetição de código são apenas algumas das vantagens da utilização desta tecnologia. A Microsoft disponibilizou no Entity Framework 4 uma forma de utilizalar-mos nossas Stored Procedures como se fossem métodos do objeto de mapeamento e a capacidade de retornar os mais variados tipos podendo ser entidades já mapeadas, valores simples ou até entidades complexas, veremos a seguir como trabalhar com estes mais variados tipos de Stored Procedures:

Os exemplos a seguir serão baseados nas entidades mostradas na Figura 1:

Figura 1. Diagrama de Entidades Mapeadas

1- Stored Procedure simples

Vamos iniciar nosso exemplo com uma SP simples que retorna todos os dados de uma entidade, por exemplo de produtos, um velho e não recomendando select * from servirá como exemplo conforme o código SQL na listagem 1:


USE example
GO
CREATE PROC RetornaProdutos
AS
SELECT * FROM Produto

Listagem 1. SQL para criação da Procedure Simples

Após a criação da procedure, vamos mapear nossa SP para o EDMX, neste primeiro momento realizar este procedimento em um arquivo de mapemaneto já existente, posteriormente mostrarei como se fazer em um arquivo novo.  O processo de mapeamento é bem simples, basta abrir-mos nosso EDMX e clicar-mos com o botão direito em qualquer área em branco, então selecionar Update Model from Database… conforme a Figura 2.

Figura 2. Atualizando o Modelo de dados

O Update Wizard irá abrir e checar as modificações do banco de dados. Após a verificação deve-se expandir á árvore de objetos na aba Add, no nó Stored Procedures e selecionar a SP que criamos conforme a Figura 3 e então aperta-se o Finish.

Figura 3. Selecionando a SP

Até o momento mapeamos a SP para nosso EDMX, mas ainda não podemos usa-la, pois precisamos agora importar a SP como função, para isto, ainda no EDMX,  deve-mos abrir a aba Model Browser que geralmente fica perto da velha conhecida Solution Explorer. Caso ela não esteja visível, pode-se utilizar o atalho do teclado apertando simultaneamente o CTRL + 1.

Na aba que se abriu, veja que existe uma pasta dentro do banco de dados mapeado chamada Stored Procedure, dentro dela constam todas as SP mapeadas. Vamos clicar em cima da RetornaProdutos com o botão direito e selecionar Add Function Import conforme a Figura 4.

Figura 4. Importando a SP como função

A tela que se abriu é aonde se define detalhes importantes como o nome da função, a SP que está sendo importada e principalmente o tipo de retorno da SP. Se nada for selecionado a função será executada porem nada será retornado, o que pode funcionar para SP que executam alguma modificação no banco (por exemplo uma inserção). Para nosso exemplo que vamos retornar uma entidade inteira, para isto vamos selecionar produto no campo Entities conforme a Figura 5 e depois apertar o OK e o Salvar no mapeamento.

Figura 5. Selecionando o Retorno

Pronto, agora já podemos utilizar nossa SP como uma função, segue na Listagem 2 um exemplo de chamada do método retornaProdutos.


exampleEntities ex = new exampleEntities();

var retorno = ex.RetornaProdutos();
foreach (Produto v in retorno)
{

}

Listagem 2. Código para chamar a função

2- Stored Procedure com parâmetros

Sendo agora mais resumido, vamos alterar a SP anterior adicionando a possibilidade de filtrar os produtos por nome, segue na Listagem 3 o código SQL da alteração


USE example
GO
ALTER PROC RetornaProdutos
@Nome nvarchar(255)
AS
SELECT * FROM Produto
WHERE Nome like '%'+@Nome+'%'

Listagem 3. Alteração da SP,adicionando parâmetros

Para que a modificação seja mapeada pelo EDMX, é necessário que sejam refeitos todos os passos mostrados no exemplo anterior, a diferença será no momento de selecionar a SP na tela Update Wizard, pois a SP não estará mais visível na aba Add, mas na aba Refresh. Ao apertar o botão Finish, o modelo será recarregado e já podemos utilizar nossa SP com as modificações realizadas conforme a Listagem 4.


var retorno = ex.RetornaProdutos("Celular");
foreach (Produto v in retorno)
{

}

Listagem 4. Utilizando a SP com o parâmetro

3- Retornando Tipos Complexos

Diferente de entidades, que conceitualmente representam tabelas do banco de dados, os tipos complexos são objetos que se assemelham as classes com suas propriedades definidas mas que não contem funções, ou seja, não tem métodos. A criação de um Tipo Complexo tem várias finalidades dentro do LINQ to Entities, para o nosso exemplo, ela será responsável por obter o retorno de uma Stored Procedure que faz um join entre várias tabelas.  Segue na Listagem5 o código SQL para a criação da SP.


USE example
GO
CREATE PROC RETORNAVENDASPORCOMPRADOR
AS
SELECT pessoa.Nome,produto.Nome,produto.Valor,venda.DtVendas FROM Pessoa pessoa
inner join Vendas venda on pessoa.Id = venda.Pessoa_Id
inner join Produto produto on produto.Id = venda.Produto_Id

Listagem 5. Stored Procedure com joins

Agora que já criamos nossa SP, vamos seguir os passos citados no Exemplo 1 para o mapeamento da SP no EDMX como função. Somente no Add Function Import os passos serão diferentes, pois conforme visto acima, estamos retornado em uma única consulta dados de várias tabelas e isto não está mapeado como um retorno no LINQ. Para isto, vamos criar um tipo complexo com o retorno esperado. Este procedimento é bastante simples bastando apenas que clique-mos nos botões, Get Column Information para que o Visual Studio analise a SP e traga os dados referentes as colunas retornadas e depois no botão Create New Complext Type conforme a Figura 6.  Apertar o OK e o Salvar.

Figura 6. Criando o Tipo Complexo

E para finalizar, segue na Listagem 6 o código chamando a nova procedure e já retornado o Complex Type que cria-mos para este propósito.


var retornoTC = ex.RETORNAVENDASPORCOMPRADOR();
string NomeCliente, NomeProduto;
DateTime dtVendas;
double ValorVenda;
foreach (RETORNAVENDASPORCOMPRADOR_Result v in retornoTC)
{
NomeCliente = v.Cliente;
NomeProduto = v.Produto;
dtVendas = v.DtVendas;
ValorVenda = v.Valor;
}

Listagem 6. Chamando a SP com Tipo Complexo

Espero ter demonstrado um pouco da facilidade que é trabalhar com Stored Procedures no LINQ to Entities, o poder que isto pode agregar as aplicações e as funcionalidades básicas que podem ser utilizadas com está tecnologia. Até a proxima.

 

USE example

GO

CREATE PROC RetornaProdutos

AS

SELECT * FROM Produto

Publicado em LINQ | Deixe um comentário

Ver código SQL gerado pelo Entity Framework

Uma dica rápida,  sabia que é possível ver o código SQL que será executado no banco de dados de uma consulta feita com LINQ to Entities? É uma excelente forma de poder ver o que irá acontecer, ver se sua query em LINQ é realmente o que você quer e, em caso de erros, conseguir realizar mais facilmente um trace da aplicação sem ter de recorrer a opções como um Profiler no servidor SQL.

Segue na Listagem 1 uma consulta simples a uma base que já foi mostrada aqui em outro artigo:


exampleEntities ex = new exampleEntities();

var query = from c in ex.Pessoa.OfType()
 select c;

Listagem 1. Consulta em LINQ to Entities

O segredo para converter esta consulta em LINQ para SQL está na classe ObjectQuery do namesapce System.Data.Objects. A Listagem 2 mostra como converter a sua consulta em Object Query e obter a consulta SQL.


ObjectQuery oQuery = (ObjectQuery)query;
 string cmdSQL = oQuery.ToTraceString();

Listagem 2.  Mostrando a consulta SQL

e como resultado temos a consulta mostrada na Listagem 3


SELECT
'0X0X' AS [C1],
[Extent1].[Id] AS [Id],
[Extent1].[Nome] AS [Nome],
[Extent2].[CPF] AS [CPF],
[Extent2].[Data_Nascimento] AS [Data_Nascimento]
FROM  [dbo].[Pessoa] AS [Extent1]
INNER JOIN [dbo].[Pessoa_PessoaFisica] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Id]

Listagem 3.  Código SQL que será executado

Publicado em LINQ | Deixe um comentário