.NET - Entity Framework 5 - Operações CRUD (revisitado) - 2
Na primeira parte do artigo definimos no nosso modelo de entidades usando o fluxo Model First e criamos as entidades Categoria e Produto e geramos o nosso banco de dados.
Vamos agora criar uma aplicação Windows Forms e realizar as operações CRUD no nosso modelo de entidades persistindo o resultado no banco de dados usando o Entity Framework 5 com LINQ to Entities.
Em nossa aplicação Windows Forms vamos implementar as seguintes funcionalidades:
Obs: Eu não vou criar uma camada de acesso aos dados para tornar o artigo mais simples visto que o objetivo é mostrar como realizar as operações CRUD usando o EF5.
Vamos aproveitar o formulário form1.vb criado no projeto Windows forms e alterar o seu nome para frmMenu.vb.
A seguir vamos incluir a partir da ToolBox o controle MenuStrip e definir um menu no formulário com as seguintes opções:
No menu PROJECT clique em Add Windows Forms e inclua os formulários : form1.vb, form2.vb, form3.vb, form4.vb e form5.vb que iremos usar no projeto.
Agora vamos incluir o código que irá implementar a chamada aos formulários do projeto:
Public Class frmMenu Private Sub ManutençãoToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ManutençãoToolStripMenuItem.Click 'manutenção categoria My.Forms.Form1.Show() End Sub Private Sub ManutençãoToolStripMenuItem1_Click(sender As Object, e As EventArgs) Handles ManutençãoToolStripMenuItem1.Click 'manutenção produtos My.Forms.Form2.Show() End Sub Private Sub ConsultaProdutosToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ConsultaProdutosToolStripMenuItem.Click 'consulta produtos por categoria My.Forms.Form3.Show() End Sub Private Sub SairToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles SairToolStripMenuItem.Click 'encerrar aplicação If (MessageBox.Show("Confirma saída ? ", "Sair", MessageBoxButtons.YesNo, MessageBoxIcon.Information) = Windows.Forms.DialogResult.Yes) Then Application.Exit() End If End Sub Private Sub ExibirToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ExibirCategoriasToolStripMenuItem.Click 'exibir categorias My.Forms.Form4.Show() End Sub Private Sub ExibirProdutosToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ExibirProdutosToolStripMenuItem.Click 'exibir produtos My.Forms.Form5.Show() End Sub End Class |
Estamos usando o recurso My.Forms do Visual Basic para simplificar a chamada aos formulários do projeto.
Manutenção de Categorias
No menu Project clique em Add Windows Forms e aceite o nome padrão form1.vb.
A seguir a partir da ToolBox inclua os seguintes controles no formulário:
Defina os controles no formulário conforme o leiaute abaixo:
Logo após o início da declaração do formulário declaramos a variável codigo que será usada em todo o formulário:
Dim codigo As Integer = Nothing
1- Definindo o código do botão Localizar
Private Sub btnLocalizar_Click(sender As Object, e As EventArgs) Handles btnLocalizar.Click If String.IsNullOrEmpty(txtCodigo.Text) Then MessageBox.Show("Informe o código da categoria", "Localizar", MessageBoxButtons.OK, MessageBoxIcon.Information) Return Else codigo = Convert.ToInt32(txtCodigo.Text) End If Try Using ctx As New ProdutoContext Dim categoria = (From cat In ctx.Categorias Where cat.CategoriaId = codigo).Single txtNome.Text = categoria.Nome End Using Catch ex As Exception MessageBox.Show("Erro " + ex.Message, "Erro::Localizar", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub |
O código usa o recurso using e cria uma nova instância do contexto usando a seguir uma consulta LINQ para retornar uma entidade categoria cujo código seja igual ao informado.
Na consulta usamos o método de extensão Single que retorna uma única entidade. Se nada for encontrado será retornado uma exceção. Para contornar isso seria mais indicado usar SingleOrDefault como veremos mais adiante.
2- Incluindo Categorias
Private Sub btnIncluir_Click(sender As Object, e As EventArgs) Handles btnIncluir.Click Try Using ctx As New ProdutoContext Dim novaCategoria As New Categoria novaCategoria.Nome = txtNome.Text ctx.Categorias.Add(novaCategoria) ctx.SaveChanges() MessageBox.Show("Categoria incluída com sucesso : " & novaCategoria.Nome.ToString, "Incluir", MessageBoxButtons.OK, MessageBoxIcon.Information) txtNome.Text = "" txtNome.Focus() End Using Catch ex As Exception MessageBox.Show("Erro " + ex.Message, "Erro::Incluir", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub |
No código criamos uma instância de Categoia e atribuímos os valores incluindo a entidade no contexto via método Add. Para persistir a nova entidade usamos o método SaveChanges().
3- Excluindo Categorias
Private Sub btnExcluir_Click(sender As Object, e As EventArgs) Handles btnExcluir.Click If String.IsNullOrEmpty(txtCodigo.Text) Then MessageBox.Show("Informe o código da categoria", "Excluir", MessageBoxButtons.OK, MessageBoxIcon.Information) Return Else codigo = Convert.ToInt32(txtCodigo.Text) End If Try Using ctx As New ProdutoContext Dim categ As Categoria = ctx.Categorias.First(Function(cat) cat.CategoriaId = codigo) txtNome.Text = categ.Nome ctx.Categorias.Remove(categ) ctx.SaveChanges() End Using Catch ex As Exception MessageBox.Show("Erro " + ex.Message, "Erro::Excluir", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub |
Aqui usamos uma expressão lambda para localizar a entidade Categoria que queremos excluir e a seguir usamos o método Remove para excluir a entidade do contexto persistindo a operação com SaveChanges.
4- Alterando Categorias
Private Sub btnAlterar_Click(sender As Object, e As EventArgs) Handles btnAlterar.Click If String.IsNullOrEmpty(txtCodigo.Text) Then MessageBox.Show("Informe o código da categoria", "Alterar", MessageBoxButtons.OK, MessageBoxIcon.Information) Return Else codigo = Convert.ToInt32(txtCodigo.Text) End If If String.IsNullOrEmpty(txtNome.Text) Then MessageBox.Show("Informe o nome da categoria", "Alterar", MessageBoxButtons.OK, MessageBoxIcon.Information) Return End If Try Using ctx As New ProdutoContext Dim categoria = (From cat In ctx.Categorias Where cat.CategoriaId = codigo).SingleOrDefault If IsNothing(categoria) Then MessageBox.Show("Categoria não localizada", "Localizar", MessageBoxButtons.OK, MessageBoxIcon.Information) Return End If categoria.Nome = txtNome.Text ctx.SaveChanges() End Using Catch ex As Exception MessageBox.Show("Erro " + ex.Message, "Erro::Alterar", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub |
Na expressão LINQ usada para localizar a entidade que vamos alterar usamos o método de extensão SingleOrDefault. Se nada for encontrado este método irá retornar Nothing (null). Por isso verificamos se a entidade categoria é null. A seguir atribuímos o novo valor a entidade categoria e salvamos o resultado com SaveChanges().
Exibindo Categorias
O formulário form4.vb será usado para exibir as Categorias cadastradas no banco de dados. Vamos apenas retornar todas as categorias exibindo-as em um controle DataGridView.
Inclua no formulário form4.vb um controle GroupBox - grpCategorias, um controle DataGridView - gdvCategorias e um controle Button - btnSair , conforme o leiaute abaixo:
A seguir inclua o código abaixo neste formulário:
Public Class Form4 Private Sub Form4_Load(sender As Object, e As EventArgs) Handles MyBase.Load carregaGrid() End Sub Private Sub carregaGrid() Dim ctx As New ProdutoContext Try Dim categorias = From cat In ctx.Categorias Select cat.CategoriaId, cat.Nome grpCategorias.Text = "Categorias : " & categorias.Count gdvCategorias.DataSource = categorias.ToList() Catch ex As Exception MessageBox.Show("Erro " + ex.Message, "Erro::Exibir", MessageBoxButtons.OK, MessageBoxIcon.Error) Finally ctx.Dispose() End Try End Sub Private Sub btnSair_Click(sender As Object, e As EventArgs) Handles btnSair.Click Me.Close() End Sub End Class |
A rotina CarregaGrid() seleciona todas as entidades categorias e exibindo o código e nome de cada uma delas no datagridview.
Manutenção de Produtos
O código da manutenção de Produtos é quase idêntico ao das Categorias por isso irei apenas exibir o código para cada uma das funcionalidades CRUD.
Abaixo vemos o formulário form2.vb que usa os mesmos controles do formulário para Categorias com exceção do controle Combobox - cboCategorias - que irá exibir as categorias cadastradas.
Logo após o início da declaração do formulário declaramos a variável codigo que será usada em todo o formulário:
Dim codigo As Integer = Nothing
1- Carregando a combobox com as categorias cadastradas
Private Sub carregaCombo() Try 'cria uma instância do Contexto Using ctx As New ProdutoContext Dim consulta = ctx.Categorias.ToList() cboCategoria.DataSource = consulta cboCategoria.DisplayMember = "Nome" cboCategoria.ValueMember = "CategoriaId" End Using Catch ex As Exception MsgBox("Erro : " & ex.Message) End Try End Sub |
Na rotina CarregaCombo obtemos todas as categorias com uma consulta LINQ e atribuímos o resultado a propriedade DataSource da Combobox definindo na sequência o nome a ser exibido com DisplayMember e o valor que será retornado da seleção com ValueMember.
A rotian carregaCombo é chamada no evento Load do formulário:
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load carregaCombo() End Sub |
A seguir temos o código para cada um dos eventos Click dos demais botões de comando: Localizar, Incluir, Excluir e Alterar
Private Sub btnLocalizar_Click(sender As Object, e As EventArgs) Handles btnLocalizar.Click If String.IsNullOrEmpty(txtCodigo.Text) Then MessageBox.Show("Informe o código do produto", "Localizar", MessageBoxButtons.OK, MessageBoxIcon.Information) Return Else codigo = Convert.ToInt32(txtCodigo.Text) End If Try Using ctx As New ProdutoContext Dim produto = (From prod In ctx.Produtos Where prod.ProdutoId = codigo).SingleOrDefault If IsNothing(produto) Then MessageBox.Show("Produto não localizado", "Localizar", MessageBoxButtons.OK, MessageBoxIcon.Information) Return End If txtNome.Text = produto.Nome txtValor.Text = produto.Valor cboCategoria.SelectedIndex = produto.CategoriaCategoriaId - 1 End Using Catch ex As Exception MessageBox.Show("Erro " + ex.Message, "Erro::Localizar", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub Private Sub btnIncluir_Click(sender As Object, e As EventArgs) Handles btnIncluir.Click Try Using ctx As New ProdutoContext Dim novoProduto As New Produto novoProduto.Nome = txtNome.Text novoProduto.Valor = Convert.ToDecimal(txtValor.Text) novoProduto.CategoriaCategoriaId = cboCategoria.SelectedIndex + 1 ctx.Produtos.Add(novoProduto) ctx.SaveChanges() MessageBox.Show("Produto incluído com sucesso : " & novoProduto.Nome.ToString, "Incluir", MessageBoxButtons.OK, MessageBoxIcon.Information) txtNome.Text = "" txtNome.Focus() End Using Catch ex As Exception MessageBox.Show("Erro " + ex.Message, "Erro::Incluir", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub Private Sub btnExcluir_Click(sender As Object, e As EventArgs) Handles btnExcluir.Click If String.IsNullOrEmpty(txtCodigo.Text) Then MessageBox.Show("Informe o código do produto", "Excluir", MessageBoxButtons.OK, MessageBoxIcon.Information) Return Else codigo = Convert.ToInt32(txtCodigo.Text) End If Try Using ctx As New ProdutoContext Dim produt As Produto = ctx.Produtos.First(Function(prod) prod.ProdutoId = codigo) txtNome.Text = produt.Nome txtValor.Text = produt.Valor cboCategoria.SelectedIndex = produt.CategoriaCategoriaId - 1 ctx.Produtos.Remove(produt) ctx.SaveChanges() End Using Catch ex As Exception MessageBox.Show("Erro " + ex.Message, "Erro::Excluir", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub Private Sub btnAlterar_Click(sender As Object, e As EventArgs) Handles btnAlterar.Click If String.IsNullOrEmpty(txtCodigo.Text) Then MessageBox.Show("Informe o código do produto", "Alterar", MessageBoxButtons.OK, MessageBoxIcon.Information) Return Else codigo = Convert.ToInt32(txtCodigo.Text) End If If String.IsNullOrEmpty(txtNome.Text) Then MessageBox.Show("Informe o nome do produto", "Alterar", MessageBoxButtons.OK, MessageBoxIcon.Information) Return End If Try Using ctx As New ProdutoContext Dim produto = (From prd In ctx.Produtos Where prd.ProdutoId = codigo).SingleOrDefault If IsNothing(produto) Then MessageBox.Show("Produto não localizado", "Alterar", MessageBoxButtons.OK, MessageBoxIcon.Information) Return End If produto.Nome = txtNome.Text produto.Valor = txtValor.Text produto.CategoriaCategoriaId = cboCategoria.SelectedIndex + 1 ctx.SaveChanges() End Using Catch ex As Exception MessageBox.Show("Erro " + ex.Message, "Erro::Alterar", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub |
Cabe destacar no código o tratamento dado a seleção feita na combobox.
Quando atribuímos o valor do código da categoria ao combobox devemos decrementar o mesmo em uma unidade visto que a propriedade SelectedIndex tem base zero e possui o primeiro elemento com valor 0 e assim sucessivamente.
cboCategoria.SelectedIndex = produto.CategoriaCategoriaId - 1
Quando damos um valor selecionado da combo à propriedade CategoriaCategoriaId da entidade produto temos que incrementar o valor de uma unidade pelo motivo explicado acima.
produto.CategoriaCategoriaId = cboCategoria.SelectedIndex + 1
Outro destaque são as conversões feitas na propriedades valor quando usamos Convert.ToDecimal quando vamos incluir a entidade no contexto.
Exibindo de Produtos
A exibição dos produtos é idêntica ao das categorias dessa forma vou apenas exibir o formulário usado e seu código:
Leiaute do formulário form5.vb para exibir produtos:
Código do formulário form5.vb:
Public Class Form5 Private Sub btnSair_Click(sender As Object, e As EventArgs) Handles btnSair.Click Me.Close() End Sub Private Sub Form5_Load(sender As Object, e As EventArgs) Handles MyBase.Load carregaGrid() End Sub Private Sub carregaGrid() Dim ctx As New ProdutoContext Try Dim produtos = From prd In ctx.Produtos Select prd.ProdutoId, prd.Nome, prd.Valor, prd.CategoriaCategoriaId grpProdutos.Text = "Produtos : " & produtos.Count gdvProdutos.DataSource = produtos.ToList() Catch ex As Exception MessageBox.Show("Erro " + ex.Message, "Erro::Exibir", MessageBoxButtons.OK, MessageBoxIcon.Error) Finally ctx.Dispose() End Try End Sub End Class |
Consultando Produtos por Categoria
O formulário form3.vb é usado para exibir os produtos para uma categoria selecionada. Para isso incluímos no formulário o controle Combobox - cboCategorias e um controle DataGridView - gdvProdutos conforme o leiaute abaixo:
No evento Load do formulário chamamos a rotina carregaCombo() que irá preencher a combobox com as categorias cadastradas:
Private Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load carregaCombo() End Sub |
O código da rotina carregaCombo() é vista abaixo:
Private Sub carregaCombo() Try 'cria uma instância do contexto Using ctx As New ProdutoContext Dim consulta = ctx.Categorias.Include("Produto").ToList() cboCategorias.DataSource = consulta cboCategorias.DisplayMember = "Nome" cboCategorias.ValueMember = "CategoriaId" End Using Catch ex As Exception MsgBox("Erro : " & ex.Message) End Try End Sub |
O código cria uma nova instância para o ObjectContext e efetua uma consulta retornando todos os objetos da entidade Categorias;
Em seguida configuramos o controle Combobox para exibir o Nome (FirstName) das categorias retornadas e tratar o valor selecionado pelo código da categoria (CategoriaId);
Agora perceba que na consulta LINQ usamos a função Include("Produto"). Mas o que significa isso ? Por que não usamos apenas ctx.Categorias.ToList() ?
Quando usamos a função Include estamos usando o recurso Eager Load.
Eager Load é o mecanismo pelo qual uma associação, coleção ou atributo é carregado imediatamente quando o objeto principal é carregado.
Usando o Eager Load explicitamente com a função Include estamos carregando a coleção de objetos Produto relacionada com cada Categoria em uma única consulta.
Como os produtos relacionados deverão ser exibidos quando uma categoria for selecionada usamos o evento SelectedIndexChanged do controle ComboBox e definimos nele o código a seguir:
Private Sub cboCategorias_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboCategorias.SelectedIndexChanged ''Obtem o objeto para a categoria selecionada Dim oCat As Categoria = CType(Me.cboCategorias.SelectedItem, Categoria) ''exibe os produtos relacionados gdvProdutos.DataSource = oCat.Produto.ToList() gdvProdutos.Columns("Categorias").Visible = False gdvProdutos.Columns("CategoriaCategoriaId").Visible = False 'redimensiona as colunas conforme o tamanho gdvProdutos.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells) End Sub |
Então quando uma categoria for selecionada obtemos o objeto para a categoria e as entidades Produto relacionadas na consulta: gdvProdutos.DataSource = oCat.Produto.ToList(). Veja abaixo exemplo mostrando o resultado.
Se não tivéssemos usando o eager load com Include("Produto"), ou seja se a consulta fosse feita usando apenas ctx.Categorias.ToList() , ao selecionar uma categoria iremos obter uma exceção visto que os produtos não foram carregados na consulta.
Se usarmos ctx.Categorias.ToList() teremos o Lazy Load, ou em outras palavras : "se o programador não solicitar a carga, o relacionamento entre entidades não será recuperado." . E neste caso os produtos não serão carregados na consulta LINQ.
Lazy Load é o mecanismo utilizado pelos frameworks de persistência para carregar informações sobre demanda. Esse mecanismo torna as entidades mais leves, pois suas associações são carregadas apenas no momento em que o método que disponibiliza o dado associativo é chamado. Assim quando objetos são retornados por uma consulta, os objetos relacionados não são carregados ao mesmo tempo, ao invés, eles são carregados automaticamente quando a propriedade de navegação for acessada. É também conhecido como "lazy loading". |
Com isso mostramos como realizar as principais operações de manutenção de dados usando o Entity Framework 5.0 e o Visual Studio 2012 Express for desktop.
Pegue o projeto completo aqui: EF5_CRUD.zip (sem as referências)
Mas como já vos disse, vós me tendes visto, e contudo não credes.João 6:36
João 6:37
Todo o que o Pai me dá virá a mim; e o que vem a mim de maneira nenhuma o lançarei fora.Referências: