Entity Framework - O mapeamento Muitos-Para-Muitos
Este série de artigos é dedicada ao Entity Framework e tem o objetivo de dar uma introdução básica e uma visão geral de como usar este importante recurso da plataforma .NET. Os artigos são parcialmente baseados na documentação da MSDN de onde são citadas as respectivas referências.
Iniciando com o Entity Framework
Conceito
A ADO .NET Entity Framework foi projetado para permitir que a criação de aplicações com acesso a dados use o modelo de programação feito contra um modelo conceitual ao invés do antigo modelo de programação feito diretamente contra um banco de dados relacional. O objetivo é diminuir a quantidade de código e o tempo de manutenção necessária exigida nestas aplicações orientada a dados.
A ADO .NET Entity Framework é uma framework que abstrai o esquema de um banco de dados relacional e o apresenta como um modelo conceitual. Abaixo a figura que representa a arquitetura em camadas do Entity Framework: (Até o lançamento do VS 2010, previsto para o primeiro trimestre.)
Data Source
: Representa a base da arquitetura onde estão armazenados os dados;
Data Providers : Os dados são acessados por um ADO.NET data provider. Até o momento tanto o SQL Server como o MySQL são suportados, e, provavelmente em breve os principais RDBMS do mercado (Oracle, MySQL, DB2, Firebird, Sybase, VistaDB, SQLite, ...) terão um provider para o EF. Entity Data Model (EDM) : O Entity Data Model é constituído de 3 partes:
Entity Client : O EntityClient é um provedor gerenciado ADO.NET que suporta o acesso a dados descritos no EDM. Ele é similar ao SQLClient, e fornece diversos componentes como EntityCommand, EntityConnection e EntityTransaction; Object Services : Este componente permite realizar consultas, atualizações, inclusões e exclusões nos dados, expressados como objetos CLR fortemente tipados que são instâncias de entity types. Ele dá suporte tanto a consultas Entity SQL como LINQ to Entities; Entity SQL (ESQL) : É uma derivação da Transact-SQL, projetada para consultar e manipular entidades definidas no EDM. Ele dá suporte herança e associação sendo que tanto os componentes Object Services como os componentes Entity Client podem executar instruções Entity SQL; LINQ to Entities : É
uma linguagem de consulta fortemente tipada para consultar entidades
definidas no EDM; |
Criar, modificar e deletar objetos e aplicar estas alterações ao banco de dados é muito fácil com o Entity Framework. Através do Object Services que gerencia todas as alterações feitas nos objetos e gera e executa as instruções T-SQL que irão realizar as operações de inclusão, alteração e exclusão contra a fonte de dados. Tudo isso é feito através da chamada do método SaveChanges do ObjectContext (equivalente ao SubmitChanges do LINQ to SQL).
Quando usamos uma ferramenta OR/M como o Entity Framework desejamos definir da melhor maneira possível o nosso modelo de entidades de forma a que tenhamos um bom desempenho e que o esforço necessário para tratar o modelo seja o mínimo possível. Como todos já sabem o modelo relacional de banco de dados não se ajusta adequadamente ao paradigma da orientação a objetos e uma ferramenta OR/M que se preze deve oferecer recursos para tornar a discrepância entre esses dois mundos o menos traumática possível.
O relacionamento Muitos-Para-Muitos é um conceito muito comum em muitas ferramentas ORM e o Entity Framework oferece suporte ao mapeamento muitos-para-muitos,s o que será mostrado neste artigo.
O relacionamento Muitos-Para-Muitos é usado em tabelas quando ambos os lados da tabela possui um relacionamento do tipo muitos. Vejamos um exemplo clássico deste tipo de relacionamento encontrado no banco de dados Northwind.mdf.
A figura abaixo exibe as tabelas Orders, OrderDetails e Products onde temos a tabela OrderDetails exibindo em ambos os lados um relacionamento do tipo muitos.
Um relacionamento
muitos-para-muitos Num relacionamento muitos para-muitos, um registo na Tabela A pode ter muitos registos coincidentes na Tabela B, e um registro na Tabela B pode ter muitos registros coincidentes na Tabela A. Esse tipo de relacionamento só é possível definindo-se uma terceira tabela (denominada tabela de associação) cuja chave primária consista em dois campos as chaves estrangeiras provenientes tanto da Tabelas A como da B. Na verdade, um relacionamento
muitos-para-muitos são dois relacionamentos um-para-muitos com uma terceira
tabela. Por exemplo, a tabela Pedidos e a tabela Produtos têm um
relacionamento muitos-para-muitos que é definido criando-se dois
relacionamentos um-para-muitos para a tabela Detalhes do Pedido. |
Neste exemplo um pedido pode possuir muitos produtos e um único produto pode estar em muitos pedidos. Neste cenário geralmente usamos uma terceira tabela a qual pode incluir os relacionamentos de ambas as tabelas Pedidos(Orders) e Produtos(Products).
Aqui temos uma tabela chamada Orders que possui todos os pedidos feitos e outra tabela Products que possui todos os produtos armazenados no banco de dados. A tabela Order Details faz a conexão entre as duas tabelas e define como cada pedido esta relacionado com o produto.
A tabela Order Details possui como chave primária os campos OrderID e ProductID pois podemos ter o mesmo pedido repetido para um dado produto.
Vamos usar o Visual Studio 2008 com SP1 e criar um projeto do tipo Windows Application com o nome EF_Muitos_Muitos usando a linguagem Visual Basic e usar o Entity Framework para efetuar o mapeamento ORM do banco de dados Northwind e veremos como o EF trata o relacionamento muitos-muitos.
Abra o VS 2008 e selecione no menu File selecione New Project e em Project Types selecione Visual Basic -> WIndows , em templates selecione Windows Forms Application e informe o nome EF_Muitos_Muitos e clique em OK;
A seguir no menu Project selecione Add New Item e em templates selecione ADO .NET Entity Data Model e informe o nome Northwind.edmx (pois vamos usar o banco de dados Northwind.mdf). Clique no botão Add;
Na próxima janela selecione o item Generate From database e clique em Next>;
Na próxima janela do assistente selecione a conexão com o banco de dados Northwind e aceite o nome padrão deixando marcado item Save entity connection settings in App.Config. Clique em Next>;
Continuando selecione as tabelas Order Details , Orders e Products e clique em Finish;
O Entity Data Model gerado pelo assistente deverá estar conforme a figura abaixo
O diagrama de entidades mostra três entidade : Products, Orders e a entidade Order_Details.
Obs: Eu alterei o nome das entidades colocando-as no singular e das Entity Set no plural assim : Entity Set Name = Orders Name = Order, etc) . Voce faz isso selecionado o modelo EDM, e no modelo selecionando a entidade e na janela de propriedades fazendo a alteração.
A entidade Order_Details está sendo exibida pois possui os atributos Quantity e Discount que são determinados pela relação entre Produtos e Pedidos se a tabela Order_Details possuísse somente os campos OrderID e ProductID ela não seria exibida no diagrama de entidades. (Esse é um benefício especial que o EF fornece quanto a tabela de junção possui somente as chaves e nenhuma coluna adicional)
Nota: Veja o artigo Entity Framework - Conceitos Básicos - Espiando o modelo conceitual para o relacionamento muitos-muitos onde a tabela de junção possui somente as chaves primárias.
Como a entidade Order_Details possui os atributos Quantity e Discount o EF a mantém no EDM gerado. Dessa forma para acessar os produtos de um pedido você vai ter que navegar através da entidade Order_Details para ter acesso a eles e vice-versa.
Vamos definir no formulário form1.vb criado por padrão uma interface bem simples para mostrar como exibir os dados relacionados em um relacionamento muitos-para-muitos.
Inclua no formulário os seguintes controles: 1 MenuStrip, 3 Labels, 3 ListBox e 1 controle Button conforme o leiaute abaixo:
O objetivo do projeto e exibir os
pedidos cadastrados em um controle ListBox quando da carga do formulário e
conforme um pedido for selecionado o seus detalhes serão exibidos em um
segundo ListBox. Após selecionar um pedido poderemos ver os seus produtos relacionados clicando no botão : Exibir Produtos para o pedido Selecionado.
|
Vejamos o código usado para alcançar este objetivo.
No evento Load do formulário temos o seguinte código:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Using dboc As New NorthwindEntities Dim pedidos = From p In dboc.Orders _ Select p lstPedidos.DisplayMember = "CustomerID" lstPedidos.ValueMember = "OrderID" lstPedidos.DataSource = pedidos.ToList End Using End Sub |
O código abaixo exibe os detalhes dos pedidos quando o usuário seleciona um pedido clicando no controle ListBox:
Private Sub lstPedidos_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstPedidos.SelectedIndexChanged Using dboc As New NorthwindEntities 'Obtém o código do pedido selecionado na ListBox lstPedidos Dim id As Integer = lstPedidos.SelectedValue 'Obtem o pedido selecionado Dim pedido = dboc.Orders.First(Function(p) p.OrderID = id) 'exibe os detalhes do pedido lstDetalhesPedidos.Items.Clear() lstDetalhesPedidos.Items.Add("OrderID : " & pedido.OrderID) lstDetalhesPedidos.Items.Add("OrderDate : " & pedido.OrderDate) lstDetalhesPedidos.Items.Add(pedido.RequiredDate) lstDetalhesPedidos.Items.Add(pedido.ShipAddress) lstDetalhesPedidos.Items.Add(pedido.ShipCity) lstDetalhesPedidos.Items.Add(pedido.ShipCountry) End Using End Sub |
Após selecionar o pedido e clicar no botão para exibir os produtos o código a seguir será executado:
Private Sub btnExibirProdutos_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExibirProdutos.Click 'limpa os itens do ListBox lstProdutos lstProdutos.Items.Clear() 'Obtém o código do pedido selecionado na ListBox lstPedidos Dim id As Integer = lstPedidos.SelectedValue Using dboc As New NorthwindEntities 'var pedido = dboc.Orders.First( p => p.OrderID == id) C# Dim pedido = dboc.Orders.First(Function(p) p.OrderID = id) 'carrega a entidade order_details relacionada pedido.Order_Details.Load() ''var produtos = pedido.Order_Details.Select(pd => pd.Products) C# Dim produtos = pedido.Order_Details.Select(Function(prd) prd.Products) 'exibe o total de produtos relacionados lblProdutos.Text = produtos.Count 'seleciona o pedido desejado Dim consulta = (From p In dboc.Orders.Include("Order_Details") _ Select p _ Where p.OrderID = id) 'percorre e exibe os produtos do pedido selecionado For Each m In consulta For Each p In m.Order_Details p.ProductsReference.Load() lstProdutos.Items.Add(p.Products.ProductName) Next Next End Using End Sub |
Vamos testar...
Acima vemos o resultado obtido após o usuário selecionar um pedido e clicar no botão para exibir os seus produtos.
Pegue o projeto completo aqui: EF_Muitos_Muitos.zip
Eu sei é apenas Entity Framework mas mas eu gosto...
Referências:
José Carlos Macoratti