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:
- Impedância objeto/relacional
- Entity Framework 4
- Anexando o banco de dados
- Configurando o ambiente
- Gerando o Entity Data Model
- Entendendo melhor o EDM
- Configurando a string de conexão
- Validando
- Criando um projeto
- Mapeamento de herança
- Windows Forms
- Criando o formulário
- Conclusão
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.
É 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; |
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.
Agora informe o caminho do banco, clicando em Browse, selecionando o arquivo desejado e escolhendo OK, como demonstrado na Figura 3.
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.
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, cliente – endereco) 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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
Esta operação permite visualizar que o arquivo não foi completamente validado, pois duas mensagens de Warning (advertência) foram geradas (Figura 17).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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().
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.
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.
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.