ASP .NET - MVC Music Store - Editando dados


Neste tutorial vamos implementar a edição dos dados usando os recursos do ASP .NET MVC 3.

Criando um controlador para gerenciar a edição dos dados(StoreManagerController)

Vamos iniciar criando um novo controlador chamado StoreManagerController o qual usará o recurso do Scaffolding disponível no ASP .NET MVC 3 para criar a estrutura básica para edição de dados em nossa aplicação.

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

Clique com o botão direito sobre a pasta Controllers e a seguir em Add->Controller;

Na janela Add Controller defina as opções conforme a figura abaixo:

Quando você clicar no botão Add, o mecanismo scaffolding do ASP.NET MVC 3 vai fazer uma boa quantidade de trabalho para você:

  1. Ele criará um novo controlador StoreManagerController com uma variável local Entity Framework;
  2. Ele adicionará uma pasta StoreManager na pasta Views do projeto;
  3. Ele incluirá as views Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml e Index.cshtml, fortemente tipadas para a classe Album;

O novo controlador StoreManagerController inclui métodos Actions que realizam as operações CRUD para o model Album e usa o contexto do Entity Framework para acessar o banco de dados.

Modificando a View criada pelo Scaffolding

É importante lembrar que embora o código tenha sido gerado automaticamente para nós ele é um código padrão, nos poupando muito tempo se tivéssemos que criar tudo manualmente, mas ele geralmente precisa ser alterado para melhor se ajustar às nossas necessidades.

Vamos começar alterando a view Index do controlador StoreManagerController que esta na pasta /Views/StoreManager/Index.vbhtml.

Esta view irá apresentar uma tabela que lista os álbuns da nossa loja com links para Editar, Detalhar e Deletar incluindo as propriedades públicas do Album definidas na classe Album.

Vamos remover o campo AlbumArtUrl entre os elementos <th></th> da tabela alterando o código conforme abaixo:

@MOdelType IEnumerable(Of MvcMusicStore.Album)

@Code
    ViewData("Title") = "Index"
End Code

<h2>Index</h2>

<p>
    @Html.ActionLink("Criar Novo", "Create")
</p>
<table>
    <tr>
        <th> Genero </th>
        <th> Artista </th>
        <th> Titulo </th>
        <th> Preco </th>
        <th></th>
    </tr>
@For Each item In Model
    Dim currentItem = item
    @<tr>
        <td> @Html.DisplayFor(Function(modelItem) currentItem.Genero.Nome) </td>
        <td> @Html.DisplayFor(Function(modelItem) currentItem.Artista.Nome) </td>
        <td> @Html.DisplayFor(Function(modelItem) currentItem.Titulo) </td>
        <td> @Html.DisplayFor(Function(modelItem) currentItem.Preco) </td>
        <td>
            @Html.ActionLink("Edit", "Edit", New With {.id = currentItem.AlbumId}) |
            @Html.ActionLink("Details", "Details", New With {.id = currentItem.AlbumId}) |
            @Html.ActionLink("Delete", "Delete", New With {.id = currentItem.AlbumId})
        </td>
    </tr>
Next
</table>

Agora, execute o aplicativo e navegue até /StoreManager. Isto mostra view Index que modificamos, mostrando uma lista dos álbuns da loja com links para Editar, Detalhes e Excluir.

Se você clicar no link Edit um formulário para editar os campos para o álbum será exibido incluindo caixas de listagem para Genero e Artista;

Obs: Podemos alterar também os textos fazendo uma tradução para o português. Deixo isso a seu critério.

Clicando no link Back List e a seguir no link Details temos os detalhes de um álbum individual:

Clicando novamente em Back to List e a seguir no link Delete teremos a solicitação de confirmação para excluir o álbum selecionado:

Vemos assim que temos as funcionalidades CRUD já definidas e criadas automaticamente no controlador StoreManagerController.

Analisando e entendendo o código do controlador StoreManagerController

O controlador criado contém uma boa quantidade de código. Ele inclui um namespace para um controlador MVC bem como uma referência ao nosso namespace Models. O controlador possui uma instância privada de MusicStoreEntities usada por cada uma das Actions do controlador para o acesso aos dados.

Imports System.Data.Entity
Imports MvcMusicStore

Namespace MvcMusicStore

    Public Class StoreManagerController
        Inherits System.Web.Mvc.Controller

    
   Private db As MusicStoreEntities = New MusicStoreEntities
        ...   

As Actions Index e Details

A action Index recupera uma lista de álbuns, incluindo cada informação de artista e gênero referenciada ao álbum. Ela esta seguindo as referências aos objetos vinculados de modo que pode exibir o nome de cada álbum, o gênero e o nome do artista , assim o controlador esta sendo eficiente e consultando esta informação na requisição original.

Esta sendo usada uma consulta LINQ com uma expressão lambda

       ' GET: /StoreManager/
        Function Index() As ViewResult
            Dim albuns = db.Albuns.Include(Function(a) a.Genero).Include(Function(a) a.Artista)
            Return View(albuns.ToList())
        End Function

A Action Details consulta pelo código id do álbum usando o método Find() retornando o resultado na view:

       ' GET: /StoreManager/Details/5
        Function Details(id As Integer) As ViewResult
            Dim album As Album = db.Albuns.Find(id)
            Return View(album)
        End Function

A Action Create

O método Action Create é um pouco diferente dos anteriores porque ela trata com a entrada do formulário.

Quando um usuário visita o site pela primeira o método /StoreManager/Create irá exibir um formulário vazio. Esta página HTML irá conter um elemento <form> que contém elementos de entrada como dropdownlist e caixa de texto onde os usuários podem entrar os detalhes do álbum.

Depois que o usuário preenche os valores do formulário ele pode pressionar o botão "Salvar" para apresentar essas alterações de volta para o nosso aplicativo e salvar no banco de dados. Quando o usuário pressionar o botão "Salvar" o <form> irá realizar um HTTP-POST de volta para a URL /StoreManager/Create e envia os valores do formulário como parte do HTTP POST.

A ASP.NET MVC nos permite dividir a lógica destes dois cenários de invocação de URL, possibilitando-nos implementar dois métodos Create separados dentro da nossa classe StoreManagerController : um para lidar com a HTTP-GET inicial para URL /StoreManager/Create, e a outro para tratar o HTTP-POST das alterações apresentadas.

      ' GET: /StoreManager/Create
        Function Create() As ViewResult
            ViewBag.GeneroId = New SelectList(db.Generos, "GeneroId", "Nome")
            ViewBag.ArtistaId = New SelectList(db.Artistas, "ArtistaId", "Nome")
            Return View()
        End Function

        ' POST: /StoreManager/Create
        <HttpPost()>
        Function Create(album As Album) As ActionResult
            If ModelState.IsValid Then
                db.Albuns.Add(album)
                db.SaveChanges()
                Return RedirectToAction("Index")
            End If

            ViewBag.GeneroId = New SelectList(db.Generos, "GeneroId", "Nome", album.GeneroId)
            ViewBag.ArtistaId = New SelectList(db.Artistas, "ArtistaId", "Nome", album.ArtistaId)
            Return View(album)
        End Function

Passando informações para uma View utilizando ViewBag

O ViewBag nos permite passar informações para uma view sem usar um objeto de modelo fortemente tipado. Neste caso, a nossa action HTTP-GET do controlador precisa passar tanto uma lista de gêneros e artistas para o formulário preencher as caixas dropdowns e a maneira mais simples de fazer isso é devolvê-los como itens ViewBag.

O ViewBag é um objeto dinâmico, o que significa que você pode digitar ou ViewBag.Macoratti ou ViewBag.SeuNome sem escrever código para definir essas propriedades. Neste caso, o código de controlador usa ViewBag.GeneroId e ViewBag.ArtistaId de forma que os valores apresentados nas caixas de seleção serão GeneroId e ArtistaId, que são as propriedades do álbum que serão configuradas.

Estes valores das caixas de seleção são devolvidos para o formulário usando o objeto SelectList, que é construído apenas para essa finalidade. Isto é feito usando um código como a seguir:

 ViewBag.GeneroId = New SelectList(db.Generos, "GeneroId", "Nome", album.GeneroId)
 ViewBag.ArtistaId = New SelectList(db.Artistas, "ArtistaId", "Nome", album.ArtistaId)

Como você pode ver no código do método Action, três parâmetros estão sendo usados para criar esse objeto:

Com isso em mente, criar a Action Create HTTP-GET é muito simples - dois SelectLists são adicionados ao ViewBag, e nenhum objeto modelo é passado para o formulário (desde que não tenha sido criado ainda).

' GET: /StoreManager/Create
Function Create() As ViewResult
      ViewBag.GeneroId = New SelectList(db.Generos, "GeneroId", "Nome")
      ViewBag.ArtistaId = New SelectList(db.Artistas, "ArtistaId", "Nome")
Return View()
End Function

Usando os Helpers HTML para exibir os menus suspensos na View Create

Na código da View (/Views/StoreManager/Create.vbhtml), você verá que a seguinte chamada é feita para exibir um dropdown Genero:

@Html.DropDownList("GeneroId", String.Empty)

Temos aqui o que é conhecido como um auxiliar ou Helper HTML - um método de utilitário que executa uma tarefa view comum. Os Auxiliares HTML são muito úteis para manter o código das nossas view conscisas e legíveis. O helper Html.DropDownList é fornecido pelo ASP.NET MVC, mas é possível criar os nossos próprios helpers para código das view em nossa aplicação.

A chamada Html.DropDownList só precisa informar duas coisas: onde conseguir a lista para exibir e qual o valor (se houver) que deve ser pré-selecionado.

O primeiro parâmetro, GeneroId, informa ao DropDownList para procurar um valor chamado GeneroId em qualquer modelo ou ViewBag. O segundo parâmetro é usado para indicar o valor a mostrar como inicialmente selecionado na lista suspensa. Uma vez que este formulário é um formulário Create, não há valor a ser pré-selecionado e um String.Empty é informado.

Manipulação dos valores postados pelo formulário

Existem dois métodos Action associados a cada formulário. O primeiro trata a solicitação HTTP-GET e exibe o formulário. O segundo trata a solicitação HTTP POST, que contém os valores do formulário apresentados. Observe que a Action do controlador tem um atributo <HttpPost()> que diz ao ASP.NET MVC que só deve responder a solicitações HTTP-POST.

     ' POST: /StoreManager/Create
        <HttpPost()>
        Function Create(album As Album) As ActionResult
            If ModelState.IsValid Then
                db.Albuns.Add(album)
                db.SaveChanges()
                Return RedirectToAction("Index")
            End If
            ViewBag.GeneroId = New SelectList(db.Generos, "GeneroId", "Nome", album.GeneroId)
            ViewBag.ArtistaId = New SelectList(db.Artistas, "ArtistaId", "Nome", album.ArtistaId)
            Return View(album)
        End Function

Esta action possui 4 responsabilidades:

  1. Ler os valores do formulário;
  2. Verificar se os valores do formulário viola as regras de validação;
  3. Se a submissão do formulário for válida então os dados são salvos e a lista atualizada é exibida;
  4. Se a submissão for inválida, re-exibe o formulário com os erros de validação;

Lendo valores do formulário com Model Binding

A Action do controlador está processando uma submissão do formulário, que inclui valores para GeneroId e ArtistaId (da lista suspensa) e os valores de caixa de texto para o título, preço, e AlbumArtUrl.

Embora seja possível acessar diretamente os valores do formulário, uma melhor abordagem é usar os recursos do modelo de ligação (Model Binding) embutido na ASP.NET MVC.

Quando a Action do controlador tem um tipo de modelo como um parâmetro, a ASP.NET MVC tentará preencher um objeto desse tipo usando entradas de formulário (assim como os valores de rota e querystring). Ela faz isso olhando para os valores cujos nomes coincidem com as propriedades do modelo de objetos, por exemplo, ao definir um novo valor GeneroId do objeto Album, ele procura por uma entrada com o nome GeneroId. Quando você cria views usando os métodos padrão ASP.NET MVC, o formulário sempre será processado usando nomes de propriedades como nomes de campos de entrada, de modo que os nomes dos campo irão coincidir.

Validando o Model

O modelo é validado com uma simples chamada para ModelState.IsValid. Nós não adicionamos quaisquer regras de validação em nossa classe Álbum ainda - vamos fazer isso em breve - assim esta verificação não tem muito o que fazer.

O que é importante é que esta verificação ModelStat.IsValid vai adaptar-se às regras de validação que colocamos no nosso modelo, assim alterações futuras das regras de validação não necessitam de quaisquer atualizações para o código da Action do controlador.

Salvando os valores submitidos

Se a submissão do formulário passar a validação, é hora de salvar os valores no banco de dados. Usando o Entity Framework, temos apenas que adicionar o modelo a coleção Albuns e chamar o método SaveChanges para persistir os valores:

  db.Albuns.Add(album)
  db.SaveChanges()

O Entity Framework gera os comandos SQL apropriados para persistir os valores e depois de salvar os dados nós redirecionamos de volta para lista de Álbuns de modo a ver a nossa atualização. Isto é feito retornando RedirectToAction com o nome da Action que queremos exibir, neste caso o método Index.

Exibindo os erros de validação

No caso de haver uma entrada de formulário válida, os valores do dropdown são adicionados ao ViewBag e os valores do Model Binding(modelo de ligação) são passados de volta para a view para exibição. Os erros de validação são automaticamente exibidos usando o helper HTML @Html.ValidationMessageFor:

Testando o formulário Create

Vamos executar a aplicação e navegar até /StoreManager/Create - para exibir um formulário em branco o qual foi retornado pelo método HTTP GET Create do controlador StoreController e preencher algumas valores e a seguir clicar no botão Create para submeter o formulário:

Vemos os dados persistidos no banco de dados e exibidos na página.

Tratando a edição dos dados

O par Action Edit (HTTP-GET e HTTP-POST) são muito semelhantes aos métodos de Action Create. Uma vez que o cenário de edição envolve o trabalho com um álbum existente, o método Edit HTTP-GET carrega o Album baseado no parâmetro "id", passados através da rota. Este código para recuperar um álbum pelo AlbumId é o mesmo que temos que ja vimos na Action Details

Como o método Create HTTP-GET método, os valores do dropdown são retornados através do ViewBag. Isto permite-nos retornar um álbum como nosso modelo de objeto para a view (que é fortemente tipada para a classe Album) enquanto a passamos dados adicionais (por exemplo, uma lista de gêneros) através do ViewBag.

   '   ' GET: /StoreManager/Edit/5
        Function Edit(id As Integer) As ViewResult
            Dim album As Album = db.Albuns.Find(id)
            ViewBag.GeneroId = New SelectList(db.Generos, "GeneroId", "Nome", album.GeneroId)
            ViewBag.ArtistaId = New SelectList(db.Artistas, "ArtistaId", "Nome", album.ArtistaId)
            Return View(album)
        End Function

A Action Edit HTTP POST é muito semelhante a Action Create HTTP POST. A única diferença é que em vez de adicionar um novo álbum para a coleção db.Albums, estamos descobrindo a instância atual do Álbum usando db.Entry(album) e definindo seu estado para Modified. Isto diz Entity Framework que estamos modificando um álbum existente em vez de criar um novo.

     ' POST: /StoreManager/Edit/5
        <HttpPost()>
        Function Edit(album As Album) As ActionResult
            If ModelState.IsValid Then
                db.Entry(album).State = EntityState.Modified
                db.SaveChanges()
                Return RedirectToAction("Index")
            End If
            ViewBag.GeneroId = New SelectList(db.Generos, "GeneroId", "Nome", album.GeneroId)
            ViewBag.ArtistaId = New SelectList(db.Artistas, "ArtistaId", "Nome", album.ArtistaId)
            Return View(album)
        End Function

Vamos testar esta funcionalidade rodando a aplicação e navegando para /StoreManager e a seguir clicando no link Edit.

Com isso teremos a exibição do formulário Edit pelo método HTTP-GET. Altere um valo qualquer como o preço e clique no botão Save:

Com isso o formulário é postado, os valores são salvos sendo retornada a lista de álbuns mostrando o valor atualizado.

Tratando a exclusão de dados

A exclusão de dados segue o mesmo padrão usado em Edit e Create, onde teremos uma Action para exibir o formulário de confirmação e outra para tratar submissão do formulário.

A Action Delete HTTP-GET é exatamente a mesma que a Action Details visto anteriormente:

        ' GET: /StoreManager/Delete/5
        Function Delete(id As Integer) As ViewResult
            Dim album As Album = db.Albuns.Find(id)
            Return View(album)
        End Function

Isso irá exibir um formulário que é fortemente tipado com um tipo Album, usando a view Delete:

O template Delete mostra todos os campos para o Model, mas podemos simplificar um pouco. Altere o código da view em /Views/StoreManager/Delete.vbhtml conforme abaixo:

@ModelType MvcMusicStore.Album

@Code
    ViewData("Title") = "Delete"
End Code

<h2>Confirmação de Exclusão</h2>

<p>Tem certeza de que deseja exclulir o álbum de título :
   <strong>@Model.Titulo</strong>
</p>
@Using Html.BeginForm()
    @<p>
        <input type="submit" value="Delete" />
    </p>
    @<p>
        @Html.ActionLink("Retornar para Lista", "Index")
    </p>
End Using

Com isso teremos a seguinte exibição simplificada:

Clicando no botão Delete faz com que o formulário seja postado de volta ao servidor o qual executa a action DeleteConfirmed:

        ' POST: /StoreManager/Delete/5
        <HttpPost()>
        <ActionName("Delete")>
        Function DeleteConfirmed(id As Integer) As RedirectToRouteResult
            Dim album As Album = db.Albuns.Find(id)
            db.Albuns.Remove(album)
            db.SaveChanges()
            Return RedirectToAction("Index")
        End Function

Nossa Action Delete HTTP-POST realiza as seguintes ações:

  1. Carrega o álbum pelo id;
  2. Delete o álbum e salva as mudanças;
  3. Redireciona para Index, exibindo que o álbum foi removido da lista;

Usando um Helper HTML customizado para truncar texto

Na exibição da view Index tanto o título do álbum como o nome do artista podem ser muito extensos afetando a formatação da exibição do leiaute da tabela na página.

Vamos criar um Helper HTML que irá permitir truncar estas e outras propriedades em nossas Views.

Com a sintaxe @helper Razor ficou muito fácil criar suas próprias funções Helper para usar em suas views.

Abra a view /Views/StoreManager/Index.vbhtml e adicione o seguinte código logo após a linha @ModelType:

@helper Truncate(ByVal input As String, ByVal length As Integer)
If input.Length <= length Then
            @input
Else
            @input.Substring(0, length)
End If
End Helper

Este método Helper toma uma string e o tamanho máximo permitido. Se o texto fornecido for menor que o tamanho definido, o helper exibe o texto completo. Se o texto for maior que o valor permitido o texto é truncado.

Para usar o Helper Truncate para ter certeza que o título do álbum e o nome do artista sejam truncados quando o seu tamanho for maior que 25 caracteres vamos alterar o código da view conforme abaixo:

@ModelType IEnumerable(Of MvcMusicStore.Album)

@helper Truncate(ByVal input As String, ByVal length As Integer)
If input.Length <= length Then
            @input
Else
            @input.Substring(0, length)
End If
End Helper
   
@Code
     ViewData("Title") = "Index"
End Code
<h2>Index</h2>
<p>
    @Html.ActionLink("Criar Novo", "Create")
</p>
<table>
    <tr>
        <th>Genero</th>
        <th>Artista</th>
        <th>Titulo</th>
        <th>Preco</th>
        <th></th>
    </tr>
@For Each item In Model
     Dim currentItem = item
    @<tr>
        <td>@Html.DisplayFor(Function(modelItem) currentItem.Genero.Nome)</td>
      
 <td>@Truncate(item.Artista.Nome, 25)</td>
        <td>@Truncate(item.Titulo,25)</td>

        <td>@Html.DisplayFor(Function(modelItem) currentItem.Preco)</td>
        <td>
            @Html.ActionLink("Edit", "Edit", New With {.id = currentItem.AlbumId}) |
            @Html.ActionLink("Details", "Details", New With {.id = currentItem.AlbumId}) |
            @Html.ActionLink("Delete", "Delete", New With {.id = currentItem.AlbumId})
        </td>
    </tr>
 Next
</table>

Agora ao executar nossa aplicação e exibir a lista de álbuns teremos o seguinte resultado:

No próximo tutorial vamos usa o recurso Data Annotations para o modelo de validação: ASP .NET - MVC Music Store - Usando Data Annotations para validação

Referências:


José Carlos Macoratti