Entity Framework - Revisitando o DataBinding


A vinculação de dados -  DataBinding - na plataforma .NET é uma poderosa característica que permite que elementos visuais no cliente seja conectados a uma fonte de dados como DataSet , DataViews ,  arrays , etc.  Alguns destes elementos visuais no cliente podem ser TextBox , ListBox , ComboBox , DataGrid , etc. Uma comunicação em duas vias é realizada de maneira que qualquer alteração feita na fonte de dados seja refletida imediatamente no elemento visual.

Pois bem o é possível realizarmos o DataBinding com o Entity Framework e neste artigo eu vou mostrar como isso é bem simples de ser feito.

Vantagens do DataBinding

  1. Na plataforma .NET a vinculação de dados pode ser usada para criar aplicações rapidamente com menos código e com um performance razoável;
  2. A plataforma cria automaticamente o código da vinculação para você (para vê-lo procure na seção - Windows Generated Code -; com isto você não precisa perder tempo codificando , e,  você tem ainda a flexibilidade de modificar qualquer código do jeito que você quiser;
  3. Você pode controlar o processo do DataBinding usando Eventos;

Desvantangens do DataBinding

  1. Realmente você ainda pode escrever um código mais otimizado sem usar databinding;
  2. Se você quer flexibilidade total , isto somente pode ser alcançado sem usar databinding;
  3. Você consegue um melhor desempenho e um maior controle sobre o processo de desenvolvimento sem usar o databinding;

Vamos então mostrar na prática como podemos usar o Databinding com o Entity Framework.

Para os exemplos deste artigo eu estou usando o Visual Basic 2010 Express Edition e a versão 4.0 do Entity Framework e o banco de dados Northwind.mdf do SQL Server 2008 Express Edition.

Abra o VB 2010 Express Edition e crie um novo projeto do tipo Windows Forms Application com o nome EF_DBinding;

Com o projeto criado vamos dar o primeiro passo para usar o Entity Framework incluindo um Entity Data Model(EDM) no projeto.

O EDM é um modelo entidades - relacionamentos onde:
  • Entidades - são instâncias de tipos de entidades como Clientes, Produtos os quais estão estruturados em registros e chaves;
  • Relacionamentos - são instâncias de tipos de relacionamentos que são associações entre dois ou mais tipos de entidades;

É a partir do modelo de entidades que podemos escrever código usando as diferentes APIs, como o provedor EntityClient ou o Object Services com LINQ to Entities
È no modelo de entidades que temos o mapeamento entre as tabelas do banco de dados e as entidades que representam a estrutura dos dados sem considerar a sua
forma de armazenamento.

Embora na versão 4.0 o Entity Framework agora temos o suporte a POCO realmente funcional, neste artigo eu vou usar o mapeamento gerado pelo EF através do entity data Model (EDM).

POCO - Plain Old CLR Object - são classes simples de domínio que possuem apenas get/sets e um construtor e que não dependem de nenhum framework;
as classes POCO não são obrigadas a herdar de nenhuma outra classe ou implementar nenhuma interface.Portanto as classes POCO  são independente de frameworks.

No menu Project selecione Add New Item e na janela do assistente selecione o modelo ADO .NET Entity Data Model e informe o nome Northwind.edmx clicando no botão Add;

Selecione o item : Generate From DataBase e clique em Next>;

Defina a conexão com o banco de dados Northwind.mdf e aceite o nome padrão para a entity connection e clique em Next>;

Na próxima janela do assistente selecione as tabelas Customers e Orders marcando as opções conforme mostra a figura abaixo e aceitando o nome padrão para o modelo;

Clique em Finish;

Note que estamos optando por marcar as opções para incluir a chave estrangeira no modelo e pluralizar ou singularizar os nomes dos objetos gerados.

A opção - Include foreign key columns in the model - irá incluir a coluna chave estrangeira da tabela. Se você não selecionar esta opção ainda poderá realizar esta definição posteriormente no relacionamento entre as tabelas.

Ao final teremos o EDM - Northwind.edmx - gerado consistindo das entidades Customer e Order onde temos o mapeamento para as tabelas Customers e Orders e todo o suporte necessário para realizar a persistência através das operações CRUD(Create,Update,Delete);

Agora que temos o EDM devemos criar no projeto um novo Data Source.

No menu Data selecione Add New Data Source...

No assistente selecione a opção Object e clique em Next>;

Na janela seguinte expanda os objetos referenciados e selecione o objeto Customer e clique em Finish;

Ao final você deverá ver na janela Data Sources os objetos Customer e Orders conforme a figura a seguir:

Dessa forma já temos toda a infra-estrutura montada para usar o DataBinding com o Entity Framework, observe que até o momento estamos usando apenas o recurso arrastar e soltar;

Vamos voltar ao formulário form1.vb do projeto que criamos no início e vamos incluir neste formulário os seguintes controles:

A seguir defina o seguinte leiaute no formulário usando estes controles:

Com a interface definida podemos partir para a definição do DataBinding.

Primeiro vamos vincular a entidade Customer ao controle ComboBox - cboCustomers - de forma a exibir todos os clientes (customers) da tabela Customers;

Selecione o formulário form1.vb e clique no ícone View Code da janela Solution Explorer para abrir a janela de código do formulário;

No início do formulário , logo após a definição do mesmo, vamos incluir a linha de código que irá criar uma instância do nosso Context gerado pelo EDM;

Dim db As New NORTHWNDEntities

O nosso contexto chama-se NORTHWNDEntities e através dele poderemos usar todos os recursos gerados no EDM;

No Entity Framework, instâncias de ObjectContext encapsulam a conexão com o banco de dados (EntityConnection), o metadados que descreve o modelo (MetadataWorkspace) e um gerenciador de objetos que rastreia alterações e mantém cache de objetos persistidos (ObjectStateManager). Cada objeto retornado por uma consulta  seja usando LINQ to Entities ou Entity SQL será automaticamente anexado a um ObjectContext.

A seguir no evento Load do formulário form1.vb vamos incluir o código que irá preencher a combobox com o nome do Contato dos clientes - ContactName;

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    cboCustomers.DataSource = db.Customers.ToList()
    cboCustomers.ValueMember = "CustomerID"
    cboCustomers.DisplayMember = "ContactName"
End Sub

O código define a fonte de dados do controle ComboBox definindo uma consulta LINQ to Entities que retorna todos os objetos da entidade Customer;

Estamos exibindo o nome do contato- ContactName - na combobox e usando a chave CustomerID como valor selecionado;

Para refletir no controle DataGridView - gdvOrders - os pedidos (orders) de cada cliente selecionado na combobox vamos usar o evento SelectedIndexChanged, de forma que feita uma seleção no controle o evento será disparado.

Inclua no evento o seguinte código:

Private Sub cboCustomers_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboCustomers.SelectedIndexChanged
Dim codigo As String = ""
Try
    
codigo = cboCustomers.SelectedValue
    If codigo <> String.Empty Then
       
 Dim pedidos = From p In db.Orders Where p.CustomerID = codigo Select p.OrderID, p.OrderDate, p.Freight, p.ShipCountry
        gdvOrders.DataSource = pedidos.ToList
        lblPedidos.Text = pedidos.Count
    End If
Catch ex As Exception
    MessageBox.Show("Erro : " ex.Message)
End Try
End Sub

Vamos entender o código acima:

- obtemos o código do cliente (CutomserID) selecionado na combobox: codigo = cboCustomers.SelectedValue

- Se o código não for vazio então realizamos uma consulta LINQ to Entities :

Dim pedidos = From p In db.Orders Where p.CustomerID = codigo Select p.OrderID, p.OrderDate, p.Freight, p.ShipCountry

- Esta consulta retorna os pedidos do cliente selecionando retornando apenas os campos: OrderID, OrderDate, Freight, ShipCountry;

- Atribuímos o a relação de objetos pedidos retornados a propriedade DataSource do DataGridView;

- Contamos o número de pedidos retornados exibindo o valor em um controle Label :  lblPedidos.Text = pedidos.Count

O resultado pode ser visto na figura abaixo:

Que tal dar uma incrementada no projeto de forma que ao clicar em um pedido do DataGridView tivéssemos mais informações do pedido exibidas em outro formulário ?

Vamos lá...

Inclua um novo formulário no projeto : Menu Project -> Add Windows Forms , aceite o nome padrão form2.vb;

A seguir vamos incluir neste formulário 6 controles Labels, controles TextBox e um controle Button conforme o leiaute abaixo:

A idéia é preencher este formulário com os dados de um pedido selecionado no formulário form1.vb;

Pois bem, vamos voltar ao formulário form1.vb e no evento CellClick do controle gdvOrders incluir o seguinte código:

Private Sub gdvOrders_CellClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles gdvOrders.CellClick
   Dim orderid As Integer = Convert.ToInt32(gdvOrders.Rows(e.RowIndex).Cells(0).Value)
   Dim frm As Form2 = New Form2(orderid)
   frm.Show()
End Sub

- primeiro obtemos o valor do código do pedido (OrderID) da linha selecionada no DataGridView;

- em seguida criamos uma instância do formulário form2 passando o código do pedido e exibimos o formulário;

Então vamos voltar ao bendito formulário form2.vb e ajustá-lo para receber o parâmetro OrderID;

No formulário form2.vb vamos definir um construtor que vai receber o parâmetro com o seguinte código:

Public Sub New(ByVal codigo As Integer)
 
  ' This call is required by the designer.
    InitializeComponent()
    ' Add any initialization after the InitializeComponent() call.
    codigoPedido = codigo
End Sub

a variável codigoPedido deve ser declarada no início do formulário: Dim codigoPedido As Integer

Agora vamos usar o evento Load do formulário form2.vb para preencher os controles com os detalhes do pedido selecionado.

Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
   Using context As New NORTHWNDEntities
      Dim pedido = context.Orders.Single(Function(p) p.OrderID = codigoPedido)
      preenchecontroles(pedido)
   End Using
End Sub

Estamos usando uma instância do ObjectContext e realizando uma consulta para retornar um único pedido com base no código do pedido selecionado:

  Dim pedido = context.Orders.Single(Function(p) p.OrderID = codigoPedido)

A seguir chamamos a rotina preencheControles() passando o pedido obtido na consulta LINQ;

A rotina preencheControles() possui o seguinte código:

Private Sub preenchecontroles(ByVal pedido As Order)
   txtCodigo.Text = pedido.OrderID
   txtData.Text = pedido.OrderDate
   txtFrete.Text = pedido.Freight
   txtPais.Text = pedido.ShipCountry
   txtCidade.Text = pedido.ShipCity
   txtRegiao.Text = pedido.ShipRegion
End Sub

O código da rotina apenas exibe os valores do objeto Pedido obtido da consulta. O resultado pode ser visto na figura abaixo:

Bem, neste momento você pode estar pensando: "Mas que raios de DataBinding é esse que eu tenho que digitar código ??? Cadê o arrastar e soltar ???

Calma , agora eu vou exibir em um outro formulário os dados dos clientes e seus respectivos pedidos em uma exibição Mestre-Detalhes usando o DataSource criado e arrastando os objeto diretamente para o formulário;

Inclua um novo formulário no projeto : Menu Project -> Add Windows Forms , aceite o nome padrão form3.vb;

Abra o formulário form3.vb e a janela DataSource;

A seguir na janela DataSource defina o modo de exibição do objeto Customer como Details:

Após isso arraste e solte o objeto Customer diretamente no formulário form3.vb dispondo os campos conforme a figura abaixo:

A seguir selecione o objeto Orders (modo DataGridView) e arraste e solte-o no formulário conforme a figura abaixo:

Observe que foi criada a barra de navegação e os objetos : CustomerBindingSource, CustomerBindingNavigator e OrdersBindingSource;

Para podemos realizar a vinculação de dados entre os controles e a fonte de dados temos que incluir o seguinte código no formulário:

Dim db As NORTHWNDEntities
Private Sub Form3_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

   db = New NORTHWNDEntities
   CustomerBindingSource.DataSource = db.Customers.OrderBy(Function(c) c.CustomerID)

End Sub

Executando o projeto e abrindo o formulário veremos o databinding em ação conforme figura abaixo:

Existem outros recursos que podemos implementar usando o DataBinding com o Entity Framework mas isso fica para outro artigo.

Pegue o projeto completo aqui: EF_DBinding.zip

Eu sei é apenas VB .NET, mas eu gosto...

Referências:

José Carlos Macoratti