Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

RSS iMasters

[Resolvido] ASP .NET MVC 3 - Implementando as funcionalidades CRU

Recommended Posts

Este artigo é baseado no texto Creating an Entity Framework Data Model for an ASP.NET MVC Application (1 of 10), e sofreu adaptações e pequenos ajustes feitos por mim.

Nosso objetivo é criar uma aplicação ASP .NET MVC usando o Entity Framework e a nova tecnologia ASP .NET MVC3. Veja a seguir alguns dos novos recurso da ASP  .NET MVC 3:

  • É compatível com a ASP.NET MVC 2 - o que significa que será fácil atualizar os projetos que você está escrevendo com a MVC 2 para a MVC 3;
  • Pode ser instalada lado-a-lado com a ASP.NET MVC 2;
  • Apresenta um novo conjunto de templates de projeto;
  • Inclui um conjunto de melhorias específicas para a View; A janela Add-> View permite escolher a sintaxe que você deseja usar ao criar novos arquivos de modelos de visão;
  • Permite usar o novo recurso Razor;
  • Inclui vários aperfeiçoamentos específicos para o controlador;
  • Incrementa a API ViewData com uma nova propriedade "ViewModel" no controlador que é do tipo "dynamic" - e que permite que você use o novo suporte à linguagem dinâmica dentro de VB e C#;
  • Inclui vários tipos de novos ActionResult e seus métodos de ajuda correspondentes.

    - A ASP .NET MVC 3 inclui suporte para ligação de dados JSON;

  • Incrementa o modelo de validação e adiciona suporte para vários dos novos recursos de validação introduzidos dentro do namespace System.ComponentModel.DataAnnotations do .NET 4;
  • Oferece um melhor suporte para aplicação de injeção de dependência (DI) e integração com containers de injeção de dependência/IOC.

Iremos criar uma aplicação ASP .NET para uma faculdade que inclui algumas funcionalidades, como matricular estudantes, criar cursos e realizar as atribuições dos instrutores. Usarei o Visual Web Developer 2010 Express Edition para criar o projeto que vai mostrar como usar o Entity Framework, além dos seguintes tópicos:

  • Criação de um modelo de dados usando atributos e anotações de dados e a API fluente para o mapeamento do banco de dados;
  • Realizar operações básicas CRUD;
  • Filtragem, ordenação e agrupamento de dados;
  • Trabalhar com dados relacionados;
  • Tratamento de simultaneidade;
  • Implementação de tabela por hierarquia de herança;
  • Implementar o repositório e unidade de padrões de trabalho;
  • Realizar consultas SQL;
  • Trabalhar com classes proxy;
  • Detecção de desativação automática de mudanças;
  • Validação de desativação quando salvar as alterações.

Antes de iniciar, verifique se você possui os seguintes recursos instalados:

Na primeira parte criamos o projeto ASP .NET MVC 3 e definimos o seguinte:

  • O estilo do site;
  • Criamos o Model e definimos as entidades;
  • Criamos o contexto usando o Entity Framework;
  • Definimos a string de conexão;
  • Inicializamos o banco de dados (Estamos usando o SQL Server Compact Edition);
  • Criamos o Controller;
  • Criamos a View para exibir os estudantes cadastrados.

Neste artigo vamos revisar e customizar as operações CRUD (create, read, update e delete) que o MVC scaffolding criou de forma automática nos controllers e views na primeira parte do artigo (o scaffolding criou literalmente um esqueleto de código que iremos revisar e customizar).

O termo scaffolding é usado em programação para indicar que o código a que se refere é apenas um esqueleto usado para tornar a aplicação funcional, e se espera que seja substituído por algoritmos mais complexos à medida que o desenvolvimento da aplicação progride.(
)

Revisando e customizando o CRUD

Abra o Visual Web Developer 2010 Express Edition e no menu File clique em Open Project. A seguir, selecione o projeto que já foi criado no artigo anterior com o nome UniversidadeMacoratti e clique em OK. Observe na janela Solution Explorer a estrutura do projeto e perceba que na pasta Controllers e na pasta View/Estudante já foram criados os arquivos para realizar as operações CRUD de leitura, inclusão e exclusão de dados;

44557.gif

Se executarmos a aplicação e clicarmos na aba Estudantes, iremos obter a página a seguir; a partir da qual poderemos acionar as operações CRUD;

44559.gif

Vamos revisar e customizar as páginas criadas começando com a página Details.vbhtml, que é exibida ao clicar no link Detalhes da página cima. Abaixo vemos a página Details.vbhtml exibindo os detalhes de um Estudante:

44560.gif

O código gerado não incluiu a propriedade Matrículas, porque esta propriedade refere-se a uma coleção. Nosso objetivo será, então, exibir na página Details a relação de cursos matriculados para cada aluno - que é justamente a coleção de matrículas.

Vamos espiar o código que foi gerado no controller na pasta Controllers/EstudanteController:

'' GET: /Estudante/Details/5

Function Details(id As Integer) As ViewResult

Dim estudante As Estudante = db.Estudantes.Find(id)

Return View(estudante)

End Function

O código usa o método Find para retornar uma única entidade Estudante correspondente ao valor da chave passada para o método como o parâmetro id. O valor id vem da consulta string no hiperlink Details, que está na página Index. Um exemplo é: http://localhost:8563/Estudante/Details/2.

 

Abra a View na pasta Views/Estudantes/Details.vbhtml e você verá o código abaixo:

@ModelType UniversidadeMacoratti.UniversidadeMacoratti.Models.Estudante

 

@Code

 

ViewData("Title") = "Details"

 

End Code

 

<h2>Details</h2>

 

<fieldset>

 

<legend>Estudante</legend>

 

<div class="display-label">SobreNome</div>

 

<div class="display-field">

 

@Html.DisplayFor(Function(model) model.SobreNome)

 

</div>

 

<div class="display-label">Nome</div>

 

<div class="display-field">

 

@Html.DisplayFor(Function(model) model.Nome)

 

</div>

 

<div class="display-label">DataMatricula</div>

 

<div class="display-field">

 

@Html.DisplayFor(Function(model) model.DataMatricula)

 

</div>

 

</fieldset>

 

<p>

 

@Html.ActionLink("Edit", "Edit", New With {.id = Model.EstudanteID}) |

 

@Html.ActionLink("Back to List", "Index")

 

</p>

Note que cada campo esta sendo exibido usando um helper DisplayFor. Vamos exibir uma lista de matrículas do estudante logo abaixo da informação "data da matrícula".

Para isso, vamos incluir um trecho código nesta view antes do fechamento da tag fieldset. Abaixo, o código incluído está em negrito:

@ModelType UniversidadeMacoratti.UniversidadeMacoratti.Models.Estudante

 

@Code

ViewData("Title") = "Details"

End Code

 

<h2>Details</h2>

 

<fieldset>

<legend>Estudante</legend>

 

<div class="display-label">SobreNome</div>

<div class="display-field">

@Html.DisplayFor(Function(model) model.SobreNome)

</div>

 

<div class="display-label">Nome</div>

<div class="display-field">

@Html.DisplayFor(Function(model) model.Nome)

</div>

 

<div class="display-label">DataMatricula</div>

<div class="display-field">

@Html.DisplayFor(Function(model) model.DataMatricula)

</div>

 

<div class="display-label">

@Html.LabelFor(Function(model) model.Matriculas)

</div>

<div class="display-field">

<table>

<tr>

<th>Curso - titulo</th>

<th>Grau</th>

</tr>

@For Each item In Model.Matriculas

<tr>

<td>

@Html.DisplayFor(Function(modelItem) item.Curso.Titulo)

</td>

<td>

@Html.DisplayFor(Function(modelItem) item.Grau)

</td>

</tr>

next

</table>

</div>

</fieldset>

<p>

 

@Html.ActionLink("Edit", "Edit", New With {.id = Model.EstudanteID}) |

@Html.ActionLink("Back to List", "Index")

</p>

No código incluído temos um laço que percorre as entidades na propriedade de navegação Matriculas. Para cada entidade Matrícula, na propriedade será exibido o título do curso e o grau. Sendo que o título do curso é retornado a partir da entidade Curso, que está armazenada na propriedade de navegação Curso da entidade Matriculas.

Os dados são retornados do banco de dados de forma automática quando necessário, ou seja, estamos usando lazy loading. Como não definimos um eager loading para a propriedade de navegação Cursos, na primeira vez que tentarmos acessar a propriedade, uma consulta será enviada ao banco de dados para retornar os dados.

Obs: Para saber mais sobre lazy loading e eager loading, veja o meu artigo: Entity Framework - Usando Lazy Load e Eager Load

 

Se executarmos o projeto novamente, iremos obter a seguinte página (após nossa alteração ter sido feita):

44562.gif

Ela exibe os cursos para os quais o estudante esta matriculado exatamente como queríamos.

Ajustando a página para criar um novo estudante

Vamos realizar outro pequeno ajuste; agora, na página Controllers/EstudanteController.vb. Vamos incluir no método Action HttPost Create um bloco try/catch no bloco de código gerado pelo scaffolding. Veja abaixo o código original:

 

        '

' POST: /Estudante/Create

'

<HttpPost()>

Function Create(estudante As Estudante) As ActionResult

If ModelState.IsValid Then

db.Estudantes.Add(estudante)

db.SaveChanges()

Return RedirectToAction("Index")

End If

 

Return View(estudante)

End Function

Agora, o código alterado:

        '

' POST: /Estudante/Create

'

<HttpPost()>

Function Create(estudante As Estudante) As ActionResult

Try

If ModelState.IsValid Then

db.Estudantes.Add(estudante)

db.SaveChanges()

Return RedirectToAction("Index")

End If

Catch ex As DataException

'log de erro (inclua uma variável nome depois de DataException)

ModelState.AddModelError("", "Não foi possível salvar alterações. Tente novamente.")

End Try

Return View(estudante)

End Function

Este código inclui a entidade Estudante criada pelo ASP .NET MVC model Binder ao conjunto de entidades Estudantes e então salva as alterações no banco de dados.

O Model Binder é uma funcionalidade do ASP .NET MVC, que torna mais fácil trabalhar com dados submetidos por um formulário; o Model Binder converte os valores do formulário para os tipos do .NET Framework e passa os valores para o método action nos parâmetros. Neste caso, o Model Binder instancia uma entidade Estudante para que você use os valores das propriedades a partir da coleção Forms.

O bloco try/catch é a única diferença no código.

Assim, se uma exceção que deriva de DataException é capturada enquanto as alterações estão sendo salvas, uma mensagem de erro genérico será exibida. Esses tipos de erros têm causas externas à aplicação e geralmente não são erros de programação, por isso o usuário é avisado para tentar novamente.

O código da view correspondente na pasta Views/Estudante/Create.vbhtml é similar ao código da página Details.vbhtml, que alteramos - exceto que agora estamos usando os helpers EditorFor e ValidationMessageFor para cada campo ao invés de usar o DisplayFor. A seguir, vemos o código de Create.vbhtml:

@ModelType UniversidadeMacoratti.UniversidadeMacoratti.Models.Estudante

@Code

ViewData("Title") = "Create"

End Code

 

<h2>Create</h2>

 

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>

<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

 

@Using Html.BeginForm()

@Html.ValidationSummary(True)

@<fieldset>

<legend>Estudante</legend>

 

<div class="editor-label">

@Html.LabelFor(Function(model) model.SobreNome)

</div>

<div class="editor-field">

@Html.EditorFor(Function(model) model.SobreNome)

@Html.ValidationMessageFor(Function(model) model.SobreNome)

</div>

 

<div class="editor-label">

@Html.LabelFor(Function(model) model.Nome)

</div>

<div class="editor-field">

@Html.EditorFor(Function(model) model.Nome)

@Html.ValidationMessageFor(Function(model) model.Nome)

</div>

 

<div class="editor-label">

@Html.LabelFor(Function(model) model.DataMatricula)

</div>

<div class="editor-field">

@Html.EditorFor(Function(model) model.DataMatricula)

@Html.ValidationMessageFor(Function(model) model.DataMatricula)

</div>

 

<p>

<input type="submit" value="Create" />

</p>

</fieldset>

End Using

 

<div>

@Html.ActionLink("Back to List", "Index")

</div>

Vamos executar novamente o projeto e clicar na aba Estudantes. A seguir, clique no link "criar novo" e informe alguns dados e uma data inválida.

Ao clicar no botão Create, teremos uma mensagem de erro informando que a data é inválida:

44563.gif

Neste caso, estamos recebendo a mensagem da validação feita no cliente que é implementada via JavaScript, mas no lado do servidor a validação também foi implementada e mesmo que a validação no lado do cliente falhe, ela será capturada e uma exceção será lançada no código do servidor.

Corrigindo a data para o dia de hoje e clicando no botão Create, teremos a página exibindo o novo estudante criado:

44564.gif

Ajustando a página de edição de dados

Agora vamos fazer um pequeno ajuste na página Controllers/EstudanteController.vb. O método Edit (HttGet), que usa o método Find para retornar a entidade Estudante selecionada, permanece inalterado.

Vamos incluir um bloco try/catch no método Edit (HttPost) conforme abaixo:

        '

' POST: /Estudante/Edit/5

 

<HttpPost()>

Function Edit(estudante As Estudante) As ActionResult

If ModelState.IsValid Then

db.Entry(estudante).State = EntityState.Modified

db.SaveChanges()

Return RedirectToAction("Index")

End If

 

Return View(estudante)

End Function

 Agora o código alterado:

  '

' POST: /Estudante/Edit/5

<HttpPost()>

Function Edit(estudante As Estudante) As ActionResult

Try

If ModelState.IsValid Then

db.Entry(estudante).State = EntityState.Modified

db.SaveChanges()

Return RedirectToAction("Index")

End If

Catch ex As DataException

'log de erro (inclua uma variável nome depois de DataException)

ModelState.AddModelError("", "Não foi possível salver alterações. Tente novamente.")

End Try

Return View(estudante)

End Function

Este código é parecido com o que usamos para ajustar o método Create (HttPost), a diferença é que ao invés de incluir uma entidade criada pelo Model Binder oa conjunto de entidades, o código acima define um sinalizador na entidade indicando que ela foi alterada =>  db.Entry(estudante).State = EntityState.Modified.

Quando o método SaveChanges for chamado, o sinalizador Modified faz com que o Entity Framework crie uma instrução SQL para atualizar a linha (registro) do banco de dados. Todas as colunas do registro do banco serão atualizadas, incluindo aquela que o usuário não alterou, e os conflitos de concorrência serão ignorados.

O código na view Views/Estudante/Edit.vbhtml é similar ao que foi visto na página Create.vbhtml e não precisa ser alterado. Executando a página, após rodar a aplicação e clicar na aba Estudante, e realizando uma alteração - após clicar no link Editar e no botão Save - iremos verificar que a alteração feita pode ser vista na página Index:

44565.gif

44566.gif

Teoria : o estado das entidades e os métodos Attach e SaveChanges

Vamos dar uma pausa para falar de um assunto importante antes de continuar o nosso trabalho.

O contexto do banco se mantém informado se as entidades na memória estão em sincronia com suas linhas/ registros correspondentes no banco de dados, e esta informação determina o que acontece quando você chama o método SaveChanges. Por exemplo, quando você passa uma nova entidade para o método Add, o estado desta entidade é definido como Added, dessa forma quando você chama o método SaveChanges, o contexto do banco de dados emite um comando SQL INSERT.

Uma entidade pode estar em um dos seguintes estados:

  • Added (adicionado). A entidade ainda não existe no banco de dados. O método SaveChanges deve emitir uma instrução INSERT;
  • Unchanged (inalterado). Nada precisa ser feito com esta entidade pelo método SaveChanges. Quando você lê uma entidade do banco de dados, ela começa com esse status;
  • Modified (modificado). Alguns, ou todos, os valores das propriedades da entidade têm sido modificadas. O método SaveChanges deve emitir uma declaração UPDATE;
  • Deleted (excluído). A entidade foi marcada para exclusão. O método SaveChanges deve emitir uma declaração DELETE;
  • Detached (independente). A entidade não está sendo controlado pelo contexto de banco de dados.

Em uma aplicação desktop, mudanças de estado são definidas automaticamente. Neste tipo de aplicação, você lê uma entidade e faz alterações em alguns dos valores de suas propriedades. Isso faz com que o estado da entidade seja automaticamente alterado para Modified. Então, quando você chama SaveChanges, o Entity Framework gera uma instrução SQL UPDATE, que atualiza apenas as propriedades que você alterou.

No entanto, em uma aplicação web esta sequência é interrompida, porque a instância do contexto do banco de dados que lê uma entidade é eliminada depois que uma página é processada.

 

Quando um método Action HttpPost Edit é chamado, este é o resultado de um novo request e você tem uma nova instância do contexto; assim, você tem que definir manualmente o estado da entidade para Modified. Então, quando você chama SaveChanges, o Entity Framework atualiza todas as colunas do registro/ linha do banco de dados, porque o contexto não tem como saber quais as propriedades você alterou.

Se você quiser que a instrução SQL Update atualize somente os campos que o usuário realmente alterou, você pode salvar os valores originais de alguma forma (como campos ocultos(hidden)) para que eles estejam disponíveis quando o método HttpPost Edit for chamado. Depois, você pode criar uma entidade Estudante usando os valores originais, chamar o método Attach com a versão original da entidade, atualizar os valores e depois chamar SaveChanges.

Ajustando a página de exclusão de dados

Na pasta Controllers\EstudanteController.vb, o código modelo para o método Delete HttpGet usa o método Find para recuperar a entidade Estudante selecionada, como você viu nos métodos Details e Edit. No entanto, para implementar uma mensagem de erro personalizada, quando a chamada para SaveChanges falhar, você irá adicionar alguma funcionalidade a este método e sua view correspondente.

Como já vimos para as operações update e create, a operação delete (eliminar) requer dois métodos action. O método que é chamado em resposta a um pedido GET exibe uma visão que dá ao usuário a oportunidade de aprovar ou cancelar a operação. Se o usuário aprovar, um pedido POST é criado. Quando isso acontece, o HttpPost método Delete é chamado e, em seguida, o método realmente executa a operação de exclusão.

Vamos adicionar um bloco try-catch para o método HttPost Delete para tratar quaisquer erros que possam ocorrer quando o banco de dados for atualizado. Se ocorrer um erro, o método HttpPost Delete chama o método HttpGet Delete , passando um parâmetro que indica que ocorreu um erro. O método HttpGet Delete, então, exibe novamente a página de confirmação juntamente com a mensagem de erro, dando ao usuário a oportunidade de cancelar ou tentar novamente.

Vamos então substituir a action Delete (HttpGet) com o seguinte código que gerencia a exibição de erros:

       Public Function Delete(id As Integer, saveChangesError As System.Nullable(Of Boolean)) As ActionResult

If saveChangesError.GetValueOrDefault() Then

ViewBag.ErrorMessage = "Não foi possível salvar as alterações, tente novamente e so problema persistir avise o administrador."

End If

Return View(db.Estudantes.Find(id))

End Function

Este código aceita um parâmetro booleano opcional que indica se ele foi chamado depois de uma falha para salvar as alterações. Este parâmetro é nulo (false) quando o método HttpGet Delete é chamado em resposta a uma solicitação de página. Quando ele for chamado pelo método Delete HttpPost em resposta a um erro de atualização do banco de dados, o parâmetro é verdadeiro e uma mensagem de erro é passado para a view.

Agora, vamos substituir o método de action HttpPost Delete (chamado DeleteConfirmed) com o seguinte código, que executa a operação de exclusão e captura qualquer erro de atualização de banco de dados:

<HttpPost(), ActionName("Delete")> _

Public Function DeleteConfirmed(id As Integer) As ActionResult

Try

Dim estudante As Estudante = db.Estudantes.Find(id)

db.Estudantes.Remove(estudante)

db.SaveChanges()

Catch generatedExceptionName As DataException

'Log de erro (inclui uma varia´vel DataException)

Return RedirectToAction("Delete", New System.Web.Routing.RouteValueDictionary() From { _

{"id", id}, _

{"saveChangesError", True} _

})

End Try

Return RedirectToAction("Index")

End Function

Este código recupera a entidade selecionada e, em seguida, chama o método Remove para definir o status da entidade para Deleted. Quando SaveChanges é chamado, um comando SQL DELETE é gerado.

Se melhorar o desempenho da aplicação for uma prioridade, você poderia evitar uma consulta SQL desnecessária para recuperar a linha, substituindo as linhas de código que chama os métodos Find e Remove com o seguinte código:

Dim estudanteToDelete As New Estudante() With {.EstudanteID = id}

 

db.Entry(estudanteToDelete).State = EntityState.Deleted

Este código instancia uma entidade Estudante usando apenas o valor de chave primária e, em seguida, define o estado da entidade para Deleted. Isso é tudo que o Entity Framework necessita a fim de excluir a entidade

Como observamos, o método HttpGet Delete não apaga os dados. Executar uma operação de exclusão em resposta a um pedido GET (ou executar uma operação de edição, criação ou qualquer outra operação que altere dados) cria um risco de segurança.

Finalmente vamos alterar a view. Localize Views/Estudante/Delete.vbhtml e inclua o seguinte código entre o cabeçalho h2 e h3:

<p class="error">@ViewBag.ErrorMessage</p>

Agora execute o projeto e após clicar na guia Estudantes, selecione um item e clique no link Deletar:

44568.gif

A tela de confirmação será exibida e se você clicar no botão Delete, a página Index será apresentada sem o estudante excluído:

44570.gif

Para ter certeza de que as conexões de banco de dados estão devidamente fechadas e os recursos liberados, você deve fazer com que a instância do contexto seja descartada. É por isso que você vai encontrar um método Dispose no final da classe EstudanteController em EstudanteController.vb, como mostrado no exemplo a seguir:

Protected Overrides Sub Dispose(disposing As Boolean)

 

db.Dispose()

MyBase.Dispose(disposing)

 

End Sub

A classe base Controller já implementa a interface IDisposable, assim esse código simplesmente adiciona um Overrides (substituição) para o método Dispose (Boolean) para explicitamente descartar a instância do contexto.

É isso! Dessa forma, concluímos todos os ajustes nos controllers e views para realizar as operações CRUD (criar, ler, atualizar, excluir).

Pegue o projeto completo aqui: UniversidadeMacoratti_2.zip

 

 

 

 

 

 

 

 

 

http://imasters.com.br/artigo/23551/dotnet/asp-net-mvc-3-implementando-as-funcionalidades-crud-basicas-com-ef-parte-02

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.