Este artigo faz uma introdução ao uso da ferramenta de persistência da Microsoft, o ADO.NET Entity Framework 4 (também conhecido como EF4) o qual é utilizado no desenvolvimento de aplicações .NET com acesso a dados. Neste material é feito uso da abordagem Database-First (partindo do banco de dados), técnica esta que está presente desde a primeira versão do ADO.NET Entity Framework


Guia do artigo:

O surgimento de frameworks OR/M (Object-Relational Mapping), tais como o NHibernate e o ADO.NET Entity Framework, trouxe soluções e benefícios que minimizaram o problema da impedância, focando principalmente na produtividade e simplicidade no que se refere ao desenvolvimento de aplicações com persistência, pois seu uso remove do desenvolvedor o uso de tarefas repetitivas para essa persistência, fazendo com que o desenvolvedor se concentre nas regras de negócio.

Em qualquer desenvolvimento de sistema orientado a objetos e que utilize um banco de dados relacional para salvar suas informações. A utilização do EF4 fornece uma série de recursos para agilizar esse tipo de desenvolvimento.

Este artigo demonstra, através da implementação passo-a-passo de um exemplo, como iniciar um projeto a partir do banco de dados, realizando seu mapeamento objeto/relacional e criando uma estrutura CRUD (Create, Read, Update, Delete) de maneira totalmente automatizada. Esta é sem dúvida uma alternativa muito interessante do ponto de vista de soluções legadas, onde normalmente utiliza-se um esquema de armazenamento pronto.

Em um ambiente computacional, uma das necessidades mais básicas refere-se à persistência. Ela é a garantia da permanência das informações de uma aplicação em determinado estado, mesmo após seu encerramento. A prática mais comum para a obtenção de tal característica se faz mediante a utilização de um banco de dados relacional.

Banco de dados relacional denota um conceito, sendo este definido no SGBDR (Sistema Gerenciador de Banco de Dados Relacional), o qual possui papel muito importante na manutenção e consulta de grande volume de dados, trazendo entre outros benefícios a recuperação de falhas, velocidade nas pesquisas, acesso concorrente, integridade, independência e segurança dos dados.

A grande dificuldade gira em torno de um problema inerente à disparidade do paradigma objeto/relacional, denominada impedância de dados, que implica em um considerável esforço para transpor as diferenças existentes entre a tecnologia relacional e a orientada a objetos.

Este artigo apresenta um primeiro contato com a nova versão do OR/M da Microsoft, denominado ADO.NET Entity Framework 4, além de discutir algumas características e especificações de sua utilização no contexto do mapeamento objeto/relacional.

Impedância objeto/relacional

O modelo relacional é baseado em princípios matemáticos, tendo suas virtudes relacionadas à capacidade de armazenar grandes volumes de dados, provendo técnicas e conceitos adequados à garantia da integridade dos mesmos, inserindo na abordagem declarativa da linguagem SQL (Structured Query Language) uma maneira eficaz de codificar “O QUE” fazer.

Em contrapartida, o modelo Orientado a Objetos (OO) está fundamentalmente atrelado à engenharia de software, evidenciando a temática imperativa da orientação a objetos, a qual faz uso de conceitos como herança, polimorfismo e encapsulamento, provando assim sua concepção no intuito de expressar “COMO” fazer.

O processo de modelagem de um sistema OO e seus principais conceitos diferem na abordagem referente à tecnologia relacional, existindo muitos desafios quanto à sua utilização, incidindo no empenho em criar um esquema livre de redundâncias e ambiguidades relativas aos bancos de dados. Observam-se ainda problemas relacionados a desempenho e uma série de outras incompatibilidades, que impõem dificuldades tais como: Granularidade, Subtipos, Identidade, Associações e Navegação.

Entity Framework 4

O ADO.NET Entity Framework, é um Middleware (neologismo criado para designar camadas de software que fazem a mediação entre outros softwares) OR/M com capacidades de modelagem e acesso a dados, sendo uma evolução do pacote de tecnologias ADO.NET, habilitando os desenvolvedores a trabalharem com um esquema conceitual do modelo relacional.

Com o EF4, o mapeamento entre o modelo de objetos e o esquema específico de armazenamento pode mudar sem que seja necessária a alteração do código da aplicação, contando ainda com o auxílio de uma série de validações em tempo de compilação.

Possui portabilidade para os mais variados bancos de dados, sendo um concorrente direto do NHibernate, tendo como diferencial as várias ferramentas visuais e recursos nativos para o Visual Studio, sendo a referência da Microsoft no contexto do mapeamento objeto/relacional.

Utilizando o EF4 o desenvolvedor é encorajado a iniciar escrevendo a lógica da aplicação, fazendo uso de um modelo de objetos OO, que inclui herança, tipos complexos e relacionamentos. Veja um diagrama de sua arquitetura na Figura 1.

Nota: O ADO.NET Entity Framework 4 é a segunda versão do Entity Framework, adotando este nome por ser parte integrante do .NET 4.0.

Arquitetura do EF resumida
Figura 1. Arquitetura do EF resumida - Fonte: MSDN.

É possível destacar uma série de melhorias nesta evolução, as quais elevam este produto a uma opção muito mais viável para o uso em aplicações do mundo real. São elas:

PI (Persistence Ignorance) É possível definir seus próprios POCO's (Plain Old CLR Objects) desassociados de qualquer tecnologia específica de persistência, permitindo a alteração da camada de acesso a dados de maneira transparente;
T4 (geração de código) T4 é uma tecnologia de geração de código incorporada a partir do Visual Studio 2008 e utilizada pelo Windows Forms Designer, elevando o nível de produtividade;
Lazy Loading (carga tardia); Além de Eager Loading (carga imediata) e Explicit Loading (carga explícita), agora é possível usufruir do Lazy Loading, onde as entidades são carregadas automaticamente, por demanda;
POCO Change-Tracking (controle de alterações) Por padrão o EF4 tomará partido do estado original dos objetos POCO, para então compará-los com a versão atual antes de salvar suas alterações através do método SaveChanges. Definindo as propriedades (da entidade POCO) como virtual, tem-se um comportamento alternativo, onde o estado é continuamente monitorado e mantido em sincronia com o Object State Manager (gestor de estados do EF4);
Self-Tracking Entities (monitoramento automático) A ideia aqui é a geração de entidades sem qualquer dependência com o EF4, e que incluam mecanismos que lhes permitam acompanhar suas próprias mudanças, tanto propriedades escalares e complexas quanto referências à coleções, tudo isso independentemente do Entity Framework;
Model-First (iniciando pelo modelo) Esta técnica possibilita ao desenvolvedor começar seu projeto criando um modelo para as suas entidades e então o Visual Studio 2010 se responsabiliza pela geração de toda a DDL (Data Definition Language) necessária para criar um banco de dados com todas as tabelas e relacionamentos;
Code-First (iniciando pelas classes POCO) Internamente, o EF4 depende de metadados (dados sobre dados), artefatos XML (eXtensible Markup Language) que dizem respeito ao modelo conceitual, bem como descrevem o esquema de banco de dados e seus mapeamentos com o modelo. A ideia é que o desenvolvedor escreva suas classes e deixe a cargo do EF4 a inferência do modelo conceitual, esquecendo do EDM (Entity Data Model). Este recurso tira proveito de Convention Over Configuration (convenção sobre configuração), também conhecida como Coding By Convention (codificação por convenção), onde o desenvolvedor só precisa especificar aspectos não convencionais da aplicação, permitindo que o EF4 usufrua de alguns pressupostos para fazer seu trabalho. O XML necessário é criado em tempo de execução e o desenvolvedor pode até mesmo gerar todo o esquema do banco de dados a partir deste modelo dinâmico;

Dica: Confira um artigo nesta mesma edição que trata de muitos desses recursos do EF4, como suporte a POCO’s.

Nota: O conteúdo deste artigo é baseado em Software Beta e está sujeito a pequenas alterações, sendo parte integrante do pacote Visual Studio 2010/.NET 4.0, tendo sua data de lançamento anunciada para o dia 12 de abril.

Nota: EDM é um artefato do Entity Framework responsável pelo mapeamento objeto/relacional. É utilizado a partir de um editor visual.

Nota: Para os exemplos aqui contidos, fez-se uso da instalação do Visual Studio 2010 Ultimate Beta 2, tendo em vista que o EF4 é parte integrante deste pacote, juntamente com o SQL Server 2008. Todos foram utilizados neste artigo, e o download encontra-se na sessão Links, ao fim do artigo.

Anexando o banco de dados

A ideia básica neste primeiro contato com o EF4 é apresentar como trabalhar com um banco de dados já existente, utilizando um exemplo que possibilite apresentar os principais cenários do mapeamento objeto/relacional. Baixe esse banco de dados do link no fim do artigo.

Com o arquivo do banco em mãos, é necessário conectá-lo ao Visual Studio. Para isso, selecione o menu View > Server Explorer e clique com o botão direito em Data Connections, para então clicar (agora com o botão esquerdo) em Add Connection. Seguindo a Figura 2, escolha como fonte de dados o Microsoft SQL Server Database File e clique em OK.

text
Figura 2. Escolhendo a fonte de dados.

Agora informe o caminho do banco, clicando em Browse, selecionando o arquivo desejado e escolhendo OK, como demonstrado na Figura 3.

Encontrando o
caminho do banco
Figura 3. Encontrando o caminho do banco.

Uma vez configurada a conexão com a fonte de dados, é preciso entender o que se tem até o momento, podendo inclusive fazer uso do MER (Modelo Entidade/Relacionamento) disponibilizado na Figura 4 para obter uma melhor visualização do exemplo apresentado.

Modelo
Entidade/Relacionamento
Figura 4. Modelo Entidade/Relacionamento.

Observam-se neste modelo os relacionamentos característicos de um banco de dados relacional, sendo eles Um-para-Um (cliente - pessoa_fisica, cliente - pessoa_juridica), Um-para-Muitos (estado - cidade, cidade - endereco, clienteendereco) e Muitos-para-Muitos (cliente - cliente_preferencia - preferencia).

As tabelas pessoa_fisica, pessoa_juridica e cliente_preferencia possuem uma chave primária não autoincremental, enquanto todas as outras foram criadas com chave primária Identity (no SQL Server, um atributo Identity é autoincremental). Note que para cada chave estrangeira utilizou-se uma convenção de nomenclatura, iniciando com o nome da tabela mãe acrescida da sigla ID.

Configurando o ambiente

Crie agora uma nova solução selecionando os menus File >New > Project > Other Project Types > Visual Studio Solutions e escolha a opção Blank Solution. Clique com o botão direito na Solução gerada e selecione Add > New Project > Visual C# > Windows escolhendo o template Class Library (tipo de projeto base para uma biblioteca de classes), nomeando-o DatabaseFirst. Elimine o arquivo Class1.cs que será criado automaticamente.

Para efeito de uma boa organização no projeto, crie uma pasta Mappings, selecionando agora o projeto (e não mais a solução), escolhendo Add > New Folder e então adicione as subpastas Associacao e Heranca, onde serão adicionados os EDMs (modelos).

Gerando o Entity Data Model

Dentro da subpasta Associacao, selecione Add > New Item > Visual C# > Data > ADO.NET Entity Data Model, nomeando-o EDMAssociacao.edmx, como pode ser visto pela Figura 5. Posteriormente, siga os mesmos passos que são apresentados a seguir, porém para a subpasta Heranca, nomeando o arquivo como EDMHeranca.edmx, com o qual serão feitas algumas customizações.

Criando um EDM com
o template ADO.NET Entity Data Model
Figura 5. Criando um EDM com o template ADO.NET Entity Data Model.

Surgem agora duas opções, como mostra a Figura 6. Selecione Generate from database, que vai mapear automaticamente a fonte de dados e também gerar as classes de negócio. A opção Empty model tem a visão de partir do modelo, ou seja, das classes de negócio, deixando a criação do banco de dados a cargo do EF4, porém, esta opção não será tratada neste artigo.

Gerando o modelo a
partir do banco de dados
Figura 6. Gerando o modelo a partir do banco de dados.

O próximo passo é selecionar uma conexão com o banco de dados, notando que se deve marcar a opção Save Entity Connection nomeando-a para AssociacaoConnStr, como mostra a Figura 7, e clicar em Next.

Nota: As strings de conexão utilizadas pelo Entity Framework contêm informações necessárias para que este possa conectar-se a um provedor de dados que lhe ofereça suporte, além de informações sobre os mapeamentos e metadados do EDM.

Escolhendo a conexão com o banco de
dados
Figura 7. Escolhendo a conexão com o banco de dados.

Será exibida uma mensagem (Figura 8), questionando a opção de incluir o banco de dados no projeto e criar uma cópia do mesmo. Escolha a opção Não.

Selecionando a opção de não copiar o
banco de dados
Figura 8. Selecionando a opção de não copiar o banco de dados.

Como demonstrado na Figura 9, selecione as tabelas desejadas e desmarque as opções “Pluralize or singuralize generated object names” (suporte à pluralização automática) e “Include foreign key columns in the model” (suporte a chaves estrangeiras no modelo), e inclua o nome AssociacaoModel para o Namespace (utilizado para declarar um escopo) do modelo.

A pluralização automática fornece a opção de utilização das formas singular e plural para a nomeação de entidades, coleções de entidades e associações, tornando mais legível o código do aplicativo, apesar de ainda não ter sido completamente portado para o português. Já o suporte à chave estrangeira no modelo simplifica alguns cenários como Data Binding (vinculação de dados) e desenvolvimento em camadas. O uso destas técnicas está fora do escopo deste artigo.

Incluindo as
tabelas desejadas para geração do modelo
Figura 9. Incluindo as tabelas desejadas para geração do modelo.

Será automaticamente gerado um diagrama (Figura 10) baseado nas tabelas escolhidas do banco de dados utilizado. Todos os seus atributos e relacionamentos estão expressos em Scalar Properties (são os campos simples) e Navigation Properties (são os relacionamentos).

Visualizando no O/R
Designer o modelo gerado
Figura 10. Visualizando no O/R Designer o modelo gerado.

O próximo passo é renomear o container (Figura 11) para que este tenha uma representação mais intuitiva. Altere a propriedade Entity Container Name, que pode ser acessada a partir da Properties Window, clicando em qualquer lugar vazio do O/R Designer.

Renomeando o
container
Figura 11. Renomeando o container.

Entendendo melhor o EDM

Tendo ainda como base a Figura 11, é interessante notar a existência da propriedade Code Generation Strategy (estratégia de geração de códigos), que apesar de ter como única opção de geração na versão beta a Default (Padrão), certamente disponibilizará outras opções no futuro. Pode-se ainda optar por None (não gerar nenhum código).

Outra propriedade de conhecimento necessário é a Database Generation Workflow, onde se tem opções quanto às estratégias de herança. Na versão utilizada neste artigo tem-se apenas a estratégia Table Per Type (descrita no banco de dados com tabelas separadas), mas já é possível observar evoluções, como no Entity Designer Database Generation Power Pack que habilita o trabalho com outros cenários, veja sessão Links.

Na sequência (Figura 12) pode ser observado que o EDM gerou automaticamente mais de 1400 linhas de código, bastando para isto, expandir o EDMAssociacao.edmx que se encontra dentro da subpasta Associacao, e dar um clique duplo em EDMAssociacao.designer.cs.

 Código gerado
automaticamente pelo EDM
Figura 12. Código gerado automaticamente pelo EDM.

Configurando a string de conexão

É preciso também outro arquivo muito importante, o App.config, onde na configuração da string de conexão, temos as informações referentes ao caminho em que se encontra o banco de dados e ainda os arquivos de mapeamento gerados pelo nosso EDM. Perceba que foi adicionado na Figura 13 o caminho do arquivo de propriedades que será incluído posteriormente no projeto, marcado em azul.

Entity Connection

Figura 13. Entity Connection.

Dê um clique duplo na pasta Propertiesdo projeto DatabaseFirst e na aba Settings clique no link: “This project does not contain a default settings file. Click here to create one”. O Visual Studio exibirá uma mensagem (Figura 14) dizendo que encontrou um novo valor no App.config. Isto se deve ao fato do código da string de conexão ter sido modificado manualmente anteriormente, como foi mostrado nas marcações em azul da Figura 13.

Mensagem de novos valores
adicionados ao App.config
Figura 14. Mensagem de novos valores adicionados ao App.config.

Veja que a string de conexão foi automaticamente anexada no arquivo Settings, como pode ser visto na Figura 15.Note ainda a alteração feita no modificador de acesso de Private para Public, para obter-se acesso ao mesmo a partir de outros projetos.

Propriedades
vinculadas automaticamente
Figura 15. Propriedades vinculadas automaticamente.

Validando o modelo

Antes de se testar o modelo criado, enviando e recuperando dados do banco de dados, é preciso verificar se o mapeamento foi formatado corretamente. Esta operação se faz de maneira muito simples, bastando um clique com o botão direito em qualquer área vazia do O/R Designer e escolhendo a opção Validate, assim como é exibido pela Figura 16.

Validando os
mapeamentos
Figura 16. Validando os mapeamentos.

Esta operação permite visualizar que o arquivo não foi completamente validado, pois duas mensagens de Warning (advertência) foram geradas (Figura 17).

Advertências
Figura 17. Advertências.

Isso se deve ao fato de não ter sido marcada a opção “Include foreign key models in the model” (Figura 9) e como as duas entidades compartilham a mesma chave primária, o EF4 montou uma associação. Para remover as advertências basta clicar em “delete these mappings”, mostrado na Figura 18.

Removendo o
mapeamento sobrescrito pela integridade referencial
Figura 18. Removendo o mapeamento sobrescrito pela integridade referencial.

Criando um projeto Console para inclusão de dados

Agora, com a escrita de algumas linhas de código, serão executados testes com o CRUD disponibilizado pelo Object Services (componente do Entity Framework que permite consultar, inserir, atualizar e excluir dados). Testaremos apenas algumas dessas operações.

Adicione à solução um novo projeto, selecionando os menus File > New > Project > Visual C# > Windows e escolha a opção Console Application, nomeando-a ConsoleAPP. Note que a classe Program.cs é criada automaticamente. O motivo da escolha de uma Console Application é manter a simplicidade do exemplo.

Clique com o botão direito do mouse sobre a pasta References e depois selecione Add Reference. Será exibida uma nova janela, escolha a aba Projects e selecione o projeto DatabaseFirst, clicando então em OK. Posteriormente atualize a classe Program.cs com os códigos apresentados na Listagem 1.

Listagem 1. Classe Program.cs.

  using System;
  using System.Linq;
  using DatabaseFirst.Mappings.Associacao;
  using DatabaseFirst.Properties;
   
  namespace ConsoleApp {
      class Program {
          static void Main(string[] args) {
              using (var ctx = new AssociacaoContainer(Settings.Default.AssociacaoConnStr)) {
                  var _estado = estado.Createestado(-1, "PR", "Paraná");
                  _estado.cidade.Add(cidade.Createcidade(-1, "Medianeira"));
                  ctx.estado.AddObject(_estado);
                  ctx.SaveChanges();//salvo para garantir que exista pelo menos uma cidade
   
                  var _cliente = cliente.Createcliente(-1, "Pessoa Fisica");
                  _cliente.pessoa_fisica = pessoa_fisica.Createpessoa_fisica(
                      -1, "RG", "CPF", new DateTime(1900, 1, 1));
                  ctx.cliente.AddObject(_cliente);
   
                  var e = endereco.Createendereco(-1, "Rua", "Bairro", 0000, "99999999");
                  e.cidade = ctx.cidade.FirstOrDefault();
   
                  var c = cliente.Createcliente(-1, "Pessoa Juridica");
                  c.pessoa_juridica = pessoa_juridica.Createpessoa_juridica(
                      -1, "Razão Social", "Inscrição Estadual", "CNPJ");
                  c.endereco.Add(e);
                  c.preferencia.Add(preferencia.Createpreferencia(-1, "preferencia"));
   
                  ctx.cliente.AddObject(c);
                  ctx.SaveChanges();
              }
          }
      }
  }

A cláusula using fica responsável pela liberação de recursos do objeto AssociacaoContainer, herdado de ObjectContext, e que disponibiliza dentre outros os métodos o AddObject() e o SaveChanges(). AddObject() é utilizado quando o objeto a ser inserido ainda não existe na fonte de dados, e SaveChanges() é responsável por persistir todas as atualizações na fonte de dados, reconfigurando inclusive o controle de alterações. As entidades geradas automaticamente pelo EDM possuem métodos estáticos que auxiliam a sua instanciação. Para a adição de objetos em coleções utilizou-se o método Add().

Neste exemplo trabalhou-se com uma associação entre as classes cliente e pessoa_juridica, mas o ideal seria que cliente fosse uma classe abstrata, base para pessoa_fisica (e também para pessoa_juridica), não permitindo que este fosse gravado isoladamente na base de dados.

A utilização do método de extensão FirstOrDefault, trazido do LINQ, recupera o primeiro elemento de uma sequência ou um valor padrão se a sequência não contiver nenhum elemento, justificando a referência ao namespace System.Linq.

Nota do DevMan

Os métodos de extensão, ou Extension Methods, permitem que o desenvolvedor adicione métodos a um determinado tipo sem a necessidade de alterar o código fonte original ou criar tipos derivados. Os métodos que são adicionados têm as mesmas características de um método estático, porém, a utilização dos mesmos funciona como se eles fossem métodos de instância.

É importante lembrar que não foi trabalhado tratamento de exceções neste exemplo, o que é totalmente desencorajado em uma aplicação real. Outro ponto a ser lembrado é que não foi iniciada explicitamente uma transação, prática muito comum em OR/Ms em geral, e o motivo disso é que o Entity Framework é transacional por natureza. Verifique também a utilização da palavra chave var, que declara uma variável local que atua somente no escopo de método e tem o seu tipo determinado pelo compilador.

Mapeamento de herança

Será modificada a estrutura gerada anteriormente no EDMHeranca.edmx para aderir à técnica de herança, que é muito mais elegante e simples do que trabalhar diretamente com associações. Antes de aplicar tal conceito, verifique a Figura 19, já mapeada com herança. Tem-se nela uma visão geral das ferramentas do EF4 disponibilizadas no Visual Studio, acessíveis mediante os painéis Toolbox, O/R Designer, Mapping Details, Model Browser e Properties.

Visualizando as
ferramentas e modificações realizadas

Figura 19. Visualizando as ferramentas e modificações realizadas.

Na Toolbox tem-se a opção de arrastar e soltar uma entidade, associação ou herança para o O/R Designer, que também possibilita os mesmos resultados através de clique com o botão direito em cima de qualquer área vazia do mesmo. Quando se clica em uma entidade ou relacionamento no O/R Designer, tem-se os detalhes referentes ao mapeamento na Mapping Details e ainda outras propriedades específicas na Properties. Outra maneira ainda é utilizar o Model Browser para chegar aos mesmos resultados.

Ainda na Figura 19, note que foram alterados os nomes de entidade, bem como algumas propriedades escalares e de navegação, visando uma melhor compreensão do modelo. Para renomeá-los, basta clicar com o botão direito no campo desejado, selecionando Rename na popup exibida.

Para modificar o mapeamento padrão das entidades PessoaFisica e PessoaJuridica, remova os relacionamentos FK_pessoa_fisica_cliente e FK_pessoa_juridica_cliente e então clique na entidade desejada, escolhendo seu Base Type (tipo base), como demonstrado na Figura 20.

Propriedades da
entidade PessoaFisica
Figura 20. Propriedades da entidade PessoaFisica.

Criando um projeto Windows Forms

Vamos agora ver como criar um cliente Desktop para nosso modelo. Na Solution Explorer clique com o botão direito do mouse sobre a solução e no menu popup exibido escolha a opção Add > New Project. Na linguagem Visual C# escolha a opção Windows e então Windows Forms Application, nomeando-o WinApp. O projeto é criado com um formulário vazio (Form1.cs). Elimine-o, pressionando a tecla Del com o arquivo selecionado.

Na guia Data Sources clique no link Add New Data Source. São exibidas algumas opções de acesso a dados, para este exemplo escolha Object e clique no botão Next. Agora clique em Add Reference e na tela que é exibida selecione a aba Project, escolha DatabaseFirst e em seguida clique no botão OK. A próxima etapa é selecionar os objetos desejados, como é mostrado na Figura 21.

Escolhendo as
fontes de dados
Figura 21. Escolhendo as fontes de dados.

Criando o formulário para cadastro de pessoa física

Clique com o botão direito do mouse sobre o projeto WinApp e escolha a opção Add > Windows Forms, dando a este formulário o nome de CadastroCliente.cs. Visualize novamente a guia Data Sources, note que os objetos estão disponíveis na forma de DataGridView. Observando a Figura 22, mude o DataSource PessoaFisica para Details, o que facilitará a manutenção das operações CRUD, e em seguida arraste-o para o formulário.

Modificando a fonte de dados
PessoaFisica para Details
Figura 22. Modificando a fonte de dados PessoaFisica para Details.

A próxima etapa é arrastar o DataGridView Enderecos, que está relacionado com PessoaFisica, e procurar pela propriedade Columns, ou ainda clicar na sua Smart Tag e selecionar Edit Columns. Será exibida uma tela semelhante a da Figura 23. Selecione uma a uma as colunas ID, Cidade e Cliente, removendo-as através do botão Remove.

Removendo as
colunas não desejadas
Figura 23. Removendo as colunas não desejadas.

Agora arraste o Details Cidade, que está relacionado com Enderecos. Arraste ainda Preferencias, relacionado com PessoaFisica, e Preferencia, que não se relaciona diretamente com nenhum outro objeto. Por fim, adicione alguns botões, deixando o formulário CadastroCliente semelhante ao apresentado na Figura 24.

Visualizando o
formulário CadastroCliente
Figura 24. Visualizando o formulário CadastroCliente.

Note que conforme o primeiro DataSource foi arrastado ao formulário e os campos nele existentes foram trazidos, foram incluídos alguns componentes não visuais (não visíveis pelo usuário), exibidos no painel abaixo da área de design, conforme mostra a Figura 25.

Componentes de acesso a dados
inseridos no formulário
Figura 25. Componentes de acesso a dados inseridos no formulário.

Cada um dos componentes inseridos no formulário, tanto na parte visual quanto não visual, é uma instância de determinada classe. No caso dos componentes visuais, foram inseridos automaticamente objetos do tipo Label, TextBox, DateTimePicker e DataGridView, além do componente que gerou a barra de tarefas, que representa as atividades possíveis no DataSource vinculado ao formulário. Foram inseridos também componentes do tipo Button e GroupBox, porém estes manualmente. Vejamos os demais:

  • Os objetos pessoaFisicaBindingSource, enderecoBindingSource, preferenciasBindingSource e preferenciaBindingSource são instâncias de BindingSource, o qual é responsável em manter a conexão entre controles e fontes de dados. A conexão é mantida através da propriedade DataSource, que neste caso refere-se ao EDMHeranca, que representa várias tabelas. Para vincular precisamente a tabela em que os controles estarão conectados, especifica-se a tabela na propriedade DataMember do BindingSource relativo;
  • O objeto pessoaFisicaBindingNavigator é responsável pela criação da barra de tarefas, vinculando na propriedade BindingSource o pessoaFisicaBindingSource.

Verifique a implementação do método CadastroCliente_Load, responsável pelo evento Load, representado pela Listagem 2. O evento Load ocorre sempre que o formulário é carregado. Ele foi utilizado para habilitar os botões de adição (bindingNavigatorAddNewItem) e exclusão (bindingNavigatorDeleteItem) do pessoaFisicaBindingNavigator, além de instanciar a variável privada context (que é do tipo HerancaContainer) e posteriormente popular o pessoaFisicaBindingSource, utilizando o método OfType<T> do LINQ na coleção de clientes, filtrando por PessoaFisica.

Nota: Os botões de AddNewItem e DeleteItem do BindingNavigator deveriam estar habilitados por padrão, como acontece em versões anteriores do Visual Studio. Foi necessário habilitá-los manualmente no evento Load do formulário. Esse comportamento caracteriza-se como um Bug da versão Beta do Visual Studio 2010, que certamente será corrigido na versão final.

Listagem 2. Método Load do formulário CadastroCliente.

 private void CadastroCliente_Load(object sender, EventArgs e) {
    bindingNavigatorAddNewItem.Enabled = true;
    bindingNavigatorDeleteItem.Enabled = true;
   
    context = new HerancaContainer(
     DatabaseFirst.Properties.Settings.Default.HerancaConnStr);
   
     pessoaFisicaBindingSource.DataSource = context.Clientes.OfType<PessoaFisica>();
 }

Conforme mostrado na Listagem 3, os botões de adição e remoção de Preferencia e Endereco têm um comportamento muito semelhante. O método AddNew do BindingSource adiciona um novo item e o Remove deve receber como parâmetro um objeto já existente para que este seja excluído. Note que o objeto a ser removido, no caso do exemplo, é o item atualmente selecionado do DataGridView relativo.

Listagem 3. Métodos Click dos botões de adicionar e remover.

private void btnAdicionarPreferencia_Click(object sender, EventArgs e) {
   preferenciasBindingSource.AddNew();
}
   
private void btnRemoverPreferencia_Click(object sender, EventArgs e) {
   var preferenciaSelecionada = preferenciasDataGridView.CurrentRow.DataBoundItem;
   preferenciasBindingSource.Remove(preferenciaSelecionada);
}
   
private void btnAdicionarEndereco_Click(object sender, EventArgs e) {
  enderecosBindingSource.AddNew();
}
   
private void btnRemoverEndereco_Click(object sender, EventArgs e) {
  var enderecoSelecionado = enderecosDataGridView.CurrentRow.DataBoundItem;
  enderecosBindingSource.Remove(enderecoSelecionado);
}

A Listagem 4 exibe a implementação do método que representa o evento Click do botão de gravação, representado por um disquete. Observe que a primeira linha invoca o método SuspendBinding() do pessoaFisicaBindingSource. Este método suspende temporariamente a vinculação de dados. Em seguida é invocado o método SaveChanges(), responsável por salvar todas as alterações. Por último, o pessoaFisicaBindingSource executa o método ResumeBinding(), que retoma a vinculação de dados.

Nota: A suspensão da vinculação de dados foi feita para permitir que fossem feitas alterações nos campos de dados, antes que ocorresse a validação. No exemplo, um campo deve ser alterado em conformidade com o outro, mas a validação do primeiro causaria um erro no segundo.
Listagem 4. Método Click do botão de salvar.

private void pessoaFisicaBindingNavigatorSaveItem_Click(object sender, EventArgs e) {
  pessoaFisicaBindingSource.SuspendBinding();
  context.SaveChanges();
  pessoaFisicaBindingSource.ResumeBinding();
}

Preferencia e Cliente (seja PessoaFisica ou PessoaJuridica) caracterizam-se por um relacionamento Muitos-para-Muitos, portanto não existe uma obrigação de nenhum dos lados em associar-se com outro. Apesar da não obrigação, isto é possível, e o exemplo deste conceito pode ser observado nas Listagens 5 e 6.

Além de pessoaFisicaBindingSource e preferenciasBindingSource, que estão ligados como mestre-detalhe, foi utilizado o preferenciaBindingSource. Note na Listagem 5 que o método CurrentChanged() do pessoaFisicaBindingSource ocorre a cada vez que um item for alterado ou navegado. Nele é criada uma lista de preferências, que vai sendo populada com todas as preferências que ainda não foram associadas com o cliente atual.

Listagem 5. Método CurrentChanged do pessoaFisicaBindingSource.

private void pessoaFisicaBindingSource_CurrentChanged(object sender, EventArgs e) {
  var preferencias = new List<Preferencia>();
  var clienteAtual = (Cliente)pessoaFisicaBindingSource.Current;
              
  foreach (var p in context.Preferencias) {
    if (!clienteAtual.Preferencias.Contains(p)) {
      preferencias.Add(p);
    }
 }
   
 preferenciaBindingSource.DataSource = preferencias;
}

Associar uma Preferencia com um Cliente fica muito simples, pois é dado o acesso apenas às preferências ainda não associadas. Na Listagem 6 foi implementado o evento Click() do botão AssociarPreferencia, que tem a responsabilidade de recuperar o item selecionado do DataGridView de Preferencia (não confunda com o de Preferencias, associadas com Cliente), removê-lo e associá-lo ao Cliente desejado.

Listagem 6. Método Click do botão AssociarPreferencia.

private void btnAssociarPreferencia_Click(object sender, EventArgs e) {
  var preferenciaSelecionada = preferenciaDataGridView.CurrentRow.DataBoundItem;
  preferenciaBindingSource.Remove(preferenciaSelecionada);
  preferenciasBindingSource.Add(preferenciaSelecionada);
}

Na Listagem 7 a primeira instrução instancia um formulário BuscaCidade, passando a variável context em seu construtor. Posteriormente é invocado o evento ShowDialog, que é modal, em conjunto com uma verificação em seu DialogResult. Isto é feito para ser possível executar o restante do código somente em caso de OK. Em seguida é verificado se BuscaCidade encontrou alguma Cidade, finalmente capturando o Endereco atual, atribuindo a Cidade retornada e atualizando os valores do Endereco através do método ResetBindings().

Listagem 7. Método Click do botão BuscarCidade.

private void btnBuscarCidade_Click(object sender, EventArgs e) {
  var busca = new BuscaCidade(context);
     if (busca.ShowDialog() == DialogResult.OK) {
        if (busca.Cidade != null) {
           var enderecoAtual = (Endereco)enderecosBindingSource.Current;
           enderecoAtual.Cidade = busca.Cidade;
            enderecosBindingSource.ResetBindings(false);
        }
     }
}

Criando o formulário para consulta de cidades

Clique com o botão direito do mouse sobre o projeto WinApp e escolha a opção Add > Windows Forms, dando a este formulário o nome de BuscaCidade.cs. Abra a guia Data Sources e arraste para o formulário o DataSource Estado na forma de DataGridView. Em seguida, arraste Cidades, que está relacionado com Estado. Por fim, adicione os botões OK e Cancelar, deixando o formulário semelhante ao apresentado na Figura 26.

Novamente alguns componentes não visuais foram trazidos, sendo exibidos no painel abaixo da área de design, conforme mostra a Figura 27. Verifique a implementação completa do formulário BuscaCidade na Listagem 8.

Visualizando o formulário BuscaCidade
Figura 26. Visualizando o formulário BuscaCidade.
Componentes de
acesso a dados inseridos no formulário
Figura 27. Componentes de acesso a dados inseridos no formulário.
Listagem 8. BuscaCidade.

  using System;
  using System.Windows.Forms;
  using DatabaseFirst.Mappings.Heranca;
   
  namespace WinApp {
      public partial class BuscaCidade : Form {
          private HerancaContainer context;
          public Cidade Cidade { get; set; }
   
          public BuscaCidade() {
              InitializeComponent();
          }
   
          public BuscaCidade(HerancaContainer ctx)
              : this() {
              this.context = ctx;
          }
   
          private void BuscaCidade_Load(object sender, EventArgs e) {
              bindingNavigatorAddNewItem.Enabled = true;
              bindingNavigatorDeleteItem.Enabled = true;
   
              estadoBindingSource.DataSource = context.Estados;
          }
   
          private void btnOK_Click(object sender, EventArgs e) {
              if (cidadesBindingSource.Current == null) {
                  MessageBox.Show("Selecione uma cidade para ser retornada.");
              } else {
                  Cidade = (Cidade)cidadesBindingSource.Current;
                  DialogResult = DialogResult.OK;
                  Close();
              }
          }
   
          private void btnCancelar_Click(object sender, EventArgs e) {
              Close();
          }
   
          private void estadoBindingNavigatorSaveItem_Click(object sender, EventArgs e) {
              estadoBindingSource.SuspendBinding();
              context.SaveChanges();
              estadoBindingSource.ResumeBinding();
          }
      }
  }

Foi criado um segundo construtor que recebe como parâmetro uma instância de HerancaContainer, que é armazenada em uma variável privada, além do mesmo chamar o construtor padrão, através da adição do código : this() à sua assinatura. Também foi criada uma propriedade pública para armazenar a cidade pesquisada. O método Load() segue a mesma ideia apresentada anteriormente. O mesmo acontece para o evento Click do botão de salvamento. No evento Click do botão OK é feita uma verificação quanto à existência de uma cidade, se não existir, é exibida uma mensagem, caso contrário, armazena-se a cidade selecionada na propriedade do formulário referente à mesma. Finalmente o DialogResult recebe a enumeração OK, para que ele possa ser finalizado, e é chamado o método Close() do formulário. Para o evento Click do botão Cancelar apenas é chamado o método Close() do formulário. Os exemplos deste artigo finalizam na Figura 28, que mostra como fica a solução completa.

Visualizando a solução
completa
Figura 28. Visualizando a solução completa.

Conclusão

O ADO .NET Entity Framework representa um grande avanço da Microsoft para consulta e atualização de dados relacionais, tendo em vista que desde 2002 as ferramentas de manipulação de dados focavam suas estratégias no DataSet. O EF baseia-se no modelo proposto pelo Dr. Peter Chen (criador do modelo entidade/relacionamento) e tem como principal objetivo abstrair o modelo de dados relacional (banco de dados) em um modelo conceitual (classes .NET) totalmente orientado a objetos, oferecendo serviços de criação, exclusão, recuperação e modificação.

O EF possui um designer RAD (Rapid Application Development) para modelar entidades de dados (objetos de domínio) com suporte a tipos complexos. Na primeira versão, as classes geradas pelo O/R Designer eram intimamente ligadas ao Framework, o que dificultava a sua utilização em aplicações N-Tier (multicamadas), onde as entidades devem ser totalmente agnósticas com relação à maneira que serão persistidas. Existiam ainda outros pontos críticos, como a falta de suporte a POCO’s, ausência de Lazy Loading e gerenciamento de concorrência.

Segundo Roger Jennings, a partir da página 353 do livro Wrox Professional ADO.NET 3.5 with LINQ and the Entity Framework, de fevereiro de 2009, em meados de 2007 alguns MVPs (Most Valuable Professionals), que mais tarde ficariam conhecidos como “NHibernate Mafia”, alegaram a membros da equipe ADO.NET uma série de deficiências no design do Entity Framework, surgindo o ADO .NET Entity Framework Vote of No Confidence (voto de não confiança, uma espécie de carta digital, a qual você poderia assinar), que continha os diversos pontos onde a ferramenta poderia ser melhorada. Recorrendo novamente ao excelente livro de Roger Jennings, observa-se na página 354 uma citação, dizendo que a Microsoft estabelecera um conselho deliberativo (visando discutir os caminhos a serem tomados) tendo como base as seguintes fontes de inspiração:

  • Eric Evans, autor de Domain-Driven Design: Tackling Complexity in the Heart of Software;
  • Stephen Forte, diretor de estratégia na Telerik e diretor regional da Microsoft;
  • Martin Fowler, autor de Patterns of Enterprise Application Software e muitos outros livros, e criador do termo Persistence Ignorance;
  • Pavel Hruby, autor de Model-Driven Design Using Business Patterns and the Visio Stencil for UML and SysML;
  • Jimmy Nilsson, autor de Applying Domain-Driven Design and Patterns with Examples in C# and .NET e criador das regras para Persistence Ignorance.

Em sua segunda versão, apelidada de EF4, a Microsoft produziu uma ferramenta robusta, seguindo boas práticas e metodologias, endereçando várias limitações da solução anterior. Pode-se citar o suporte a classes POCO, inclusão de chaves estrangeiras no modelo, pluralização automática, Lazy Loading, Persitence Ignorance, T4 (possibilitando a customização de templates), melhor suporte a Stored Procedures, maior legibilidade e eficiência das instruções SQL geradas, melhor suporte ao LINQ, POCO Change-Tracking, Self-Tracking Entities, compatibilidade com tecnologias como DDD (Domain-Driven Design), SOA (Service-Oriented Architecture), TDD (Test-Driven Development) e outras melhorias, como a expansão dos cenários de desenvolvimento para Model-First, Code-First e N-Tier.

Mais sobre .NET