Desenvolvimento - ASP. NET

ASP.NET MVC/MVVM - Criando uma sessão de produtos

Aplicações utilizando o ASP.NET MVC vem crescendo a cada dia. O ASP.NET MVC fornece um desenvolvimento mais sutil sem perder a robustez e facilidades que o ASP.NET tem. O MVVM (Model View ViewModel) é um padrão para interfaces que tem a finalidade de desacoplar ainda mais as responsábilidades da View/Modelo.

por André Baltieri



Código fonte

Introdução

Aplicações utilizando o ASP.NET MVC vem crescendo a cada dia mais. Como disse em alguns artigos passados, o ASP.NET MVC fornece um desenvolvimento mais "sutil" sem perder a robustez e facilidades que o ASP.NET tem.

Além disso, integrado com o Visual Studio que provê templates prontos para Views e Controllers, formam uma dupla difícil de ser derrubada.

Por fim, temos a total integração com o ADO.NET Entity Framework, que provê toda a persistência dos objetos no banco de dados.

MVVM - Model View ViewModel

Antes de por a mão na massa, é legal entender que não usaremos só o MVC como comum. Existe um padrão para interfaces que se chama MVVM (Model View ViewModel) que tem a finalidade de desacoplar ainda mais as responsábilidades da View/Modelo.

Basicamente podemos falar que o MVVM serve como um "Wrapper" entre a View e o Modelo, ou seja, ao invéz de acessar o modelo, o Controller acessa o ViewModel, e a View também.

Para uma descrição melhor de MVVM acesse: http://en.wikipedia.org/wiki/Model_View_ViewModel

Especificações

Como o intuito aqui é apenas exemplificar, não vou criar muitas classes, na verdade criarei apenas duas classes, sendo elas Produto e Categoria.

Para persistência de dados utilizaremos ADO.NET Entity Framework + POCO, sendo assim caso necessário trocar este componente para NHibernate por exemplo, será muito mais fácil.

Além disso, começaremos o desenvolvimento pelo Model (Domínio) -- Muita calma nessa hora, pois DDD não é apenas começar a modelagem pelo domínio --, e depois gerar a base de dados, de acordo com o diagrama de classes.

Criando um novo projeto

Novo Projeto
Figura 1 - Novo Projeto.

Criando o modelo

Como dito préviamente, começaremos o desenvolvimento pelo model (Model), criando as duas classes que utilizaremos na aplicação, que serão Product.cs e Category.cs. Para facilitar, como toda entidade terá um identificador (Id), criaremos uma interface para definir este atributo e assim todas as classes que forem entidade devem implementar esta.

A Listagem 1 demonstra o código da interface IEntity, a Listagem 2 demonstra o código do arquivo Product.cs e a Listagem 3 demonstra o código da classe Category.cs.

 
namespace Inside.NET.Store.Models
{
    public interface IEntity
    {
        int Id { get; set; }
    }
}
Listagem 1 - Interface IEntity.cs.

 
namespace Inside.NET.Store.Models
{
    public class Product : IEntity
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual decimal Price { get; set; }
 
        private Category category = new Category();
        public virtual Category Category
        {
            get { return this.category; }
            set { this.category = value; }
        }
    }
}
Listagem 2 - Classe Product.cs.

 
using System.Collections.Generic;
 
namespace Inside.NET.Store.Models
{
    public class Category : IEntity
    {
        public virtual int Id { get; set; }
        public virtual string Title { get; set; }
 
        private IList<Product> products;
        public virtual IList<Product> Products 
        {
            get { return this.products; }
            set { this.products = value; }
        }
    }
}
Listagem 3 - Classe Category.cs.

Devemos observar que criei as propriedades com o modificador Virtual, permitindo que as mesmas sejam sobrescritas pelo EF.

Partimos então para a criação do EDMX (Entity Data Model), que ficará como na Figura 2.

Nota: Lembre-se de criar um modelo em branco e adicionar as entidades e depois desabilitar a geração de código (Code Generation Strategy - None).

Figura 2 - Entity Data Model - Arquivo StoreEntityDataModel.edmx.
Figura 2 - Entity Data Model - Arquivo StoreEntityDataModel.edmx.

Com o modelo criado, precisamos agora criar o contexto, que nada mais é do que uma classe que fará a ponte entre as classes de domínio que criamos e o Entity Framework.

Para isto, vamos adicionar mais uma classe ao modelo (Model) chamada StoreContext.cs e escrevê-la como na Listagem 4.

 
using System.Data.Objects;
 
namespace Inside.NET.Store.Models
{
    public class StoreContext : ObjectContext
    {
        /// <summary> 
        /// Construtor informando conectionstring e nome do container
        /// </summary> 
        public StoreContext() : 
           base("name=StoreEntityDataModelContainer", "StoreEntityDataModelContainer") { }
 
        /// <summary> 
        /// Conjunto de objetos do tipo produto
        /// </summary> 
        private IObjectSet<Product> products;
        public IObjectSet<Product> Products 
        {
            get
            {
                if (products == null)
                    products = CreateObjectSet<Product>();
 
                return products;
            }
        }
 
        /// <summary> 
        /// Conjunto de objetos do tipo categoria
        /// </summary> 
        private IObjectSet<Category> categories;
        public IObjectSet<Category> Categories
        {
            get
            {
                if (categories == null)
                    categories = CreateObjectSet<Category>();
 
                return categories;
            }
        }
    }
}
Listagem 4 - Classe StoreContext.cs.

Neste ponto, já temos o modelo (Domínio) gerado, precisamos então gerar nossa base de dados, porém não precisamos nos preocupar com isso, pois o Entity Framework fará isso por nós, basta abrir o StoreEntityDataModel.edmx, clicar sobre o fundo da tela e seleciona a opção Generate Database, como mostrado na Figura 3.

Figura 3 - Gerando o banco de dados.
Figura 3 - Entity Data Model - Gerando o banco de dados.

Após isto, siga as instruções para criação da ConnectionString e por fim teremos um script SQL gerado. Clique com o botão direito do mouse e selecione a opção executar, para que o banco de dados seja criado. Se preferir, copie o código e execute manualmente no SQL Server, utilizando o SQL Server Management Studio.

Criando os ViewModels

Como comentado préviamente, utilizaremos o MVVM para desacoplar a view do controller/model, sendo assim, vamos criar mais uma pasta no projeto chama ViewModels, e começar a adicionar os ViewModels do produto.

Basicamente, para cada ação (Create, Read, Update e Delete) teremos um ViewModel, e além destes, teremos mais um para listar os produtos de uma determinada categoria.

Começando pela listagem de todos os produtos, criaremos uma nova classe na pasta ViewModels chamada ProductIndexViewModel.cs, cujo código está descrito na Listagem 5.

 
using System.Collections.Generic;
using Inside.NET.Store.Models;
 
namespace Inside.NET.Store.ViewModels
{
    public class ProductIndexViewModel
    {
        public int ProductCount { get; set; }
        public List<Product> Products { get; set; }
        public List<Category> Categories { get; set; }
    }
}
Listagem 5 - Classe ProductIndexViewModel.cs.

Até aí nada de muito misterioso, temos um contador de produtos e a lista dos produtos e categorias, vamos então ver como esta classe pode ser trabalhada no controller e na view.

Criando os Controllers

Para cada entidade que criamos, também criaremos um controller, e para isto, basta clicar com o botão direito sobre a pasta controller e selecionar a opção Add > Controller.

Não se esqueça de selecionar a opção para adicionar os metodos CRUD automaticamente, como mostrado na Figura 4.

Figura 4 - Adicionando um Controller.
Figura 4 - Adicionando um Controller.

Com o controle criado, podemos codificar a método Index() que irá listar todos os produtos. A Listagem 6 ilustra seu código.

 
using System.Linq;
using System.Web.Mvc;
using Inside.NET.Store.Models;
using Inside.NET.Store.ViewModels;
 
namespace Inside.NET.Store.Controllers
{
    public class ProductController : Controller
    {
        // Cria o contexto
        StoreContext storeContext = new StoreContext();
 
        //
        // GET: /Product/
        public ActionResult Index()
        {
            var products = from prod in storeContext.Products select prod;
            var categories = storeContext.Categories.ToList();
            var productsIndexViewModel = new ProductIndexViewModel
            {
                ProductCount = products.Count(),
                Products = products.ToList<Product>(),
                Categories = categories
            };
 
            return View(productsIndexViewModel);
        }
        .
        .
        .
        .
Listagem 6 - Método Index() da classe ProductController.cs.

Criando a View inicial dos Produtos

Antes de criar a View será necessário dar um Build na aplicação. Após isto, clique com o botão direito sobre a assinatura do método Index() no arquivo ProductController.cs e selecione a opção Add > View.

Configure a View como mostrado na Figura 5.

Figura 5 - Adicionando a View do Produto.
Figura 5 - Adicionando a View do Produto.

Com a view adicionada, codifique-a como mostrado na Figura 6.

Figura 6 - View inicial dos Produtos (Index.aspx).
Figura 6 - View inicial dos Produtos (Index.aspx).

Podemos notar que por ter utilizado Strongly Typed View, temos no topo da página a herança do MVVM ProductIndexViewModel.

Para escrever na View utilizamos <%: %> (Com : [dois pontos]), como feito na linha 11, e quando não necessitamos escrever nada (Como na linha 19) simplesmente ignoramos o ":".

Para acessar o conteúdo da ViewModel, utilizamos a variável Model, como na linha 11 onde acessei a propriedade ProductCount da ViewModel.

Apenas com fins de facilitar nossos testes na View, no Solution Explorer expanda a pasta Views, Shared e abra o arquivo Site.Master. Vamos criar um link para a View dos produtos.

A Figura 7 mostra a criação de um Action Link para a View de produtos.

Figura 7 - Criando um Action Link.
Figura 7 - Criando um Action Link.

O parâmetro em vermelho representa o nome do Link, o parâmetro em verde representa a Action que será exibida, que neste caso é Index, e por fim o parâmetro em azul representa o Controller.

Se tudo ocorreu certo, neste momento você terá uma view como mostrada na Figura 8.

Figura 8 - Aplicação rodando.
Figura 8 - Aplicação rodando.

Criando o ViewModel para listar os Produtos de uma determinada categoria

Como pudemos notar na Figura 6 (Linha 30), criamos um link que referencia um outro Action, chamado Browse, e passa um parâmetro (category) para este.

Isto deve-se ao fato que criaremos também uma view para listar os produtos de uma determinada categoria, mas antes disso, precisamos criar mais uma ViewModel, chamada ProductBrowseViewModel.cs.

A Listagem 7 ilustra a codificação da ViewModel ProductBrowseViewModel.cs.

 
using System.Collections.Generic;
using Inside.NET.Store.Models;
 
namespace Inside.NET.Store.ViewModels
{
    public class ProductBrowseViewModel
    {
        public int ProductCount { get; set; }
        public List<Product> Products { get; set; }
        public Category Category { get; set; }
        public List<Category> Categories { get; set; }
    }
}
Listagem 7 - Arquivo ProductBrowseViewModel.cs.

Novamente, nada de novidades, temos um contador de produtos, uma lista de produtos, a categoria atual e uma lista de categoria. Precisamos agora criar uma action para ler os produtos de uma determinada categoria, preencher o ViewModel e enviá-lo para a View.

Criando um método para listar produtos por categoria no ProductController

Abra então o arquivo ProductController.cs e adicione o seguinte método, como descrito na Listagem 8.

 
public ActionResult Browse()
{
    string categoryTitle = Request.QueryString["category"];
 
    var category = from cat in storeContext.Categories 
		where cat.Title == categoryTitle select cat;
    var products = from prod in storeContext.Products 
		where prod.Category.Title == categoryTitle select prod;
    var categories = storeContext.Categories.ToList();
    var productsBrowseViewModel = new ProductBrowseViewModel 
        { 
            ProductCount = products.Count(), 
            Products = products.ToList<Product>(),
            Category = category.First(),
            Categories = categories
        };
 
    return View(productsBrowseViewModel);            
}
Listagem 8 - Adicionando o método Browse no arquivo ProductController.cs.

Primeiro obtemos o título da categoria via QueryString (Não quis entrar na parte de Custom Routes aqui pois pretendo escrever um artigo sobre isso posteriormente), em seguida obtemos a categoria, lista de produtos e lista de categorias utilizando LINQ.

Por fim, passamos estas variáveis para o ViewModel e fornecemos o mesmo a View.

Criando a View para o método Browse

Antes de criar a View, faça um Build da aplicação.

Após o Build, clique com o botão direito do mouse sobre a assinatura do método Browse a selecione as opções Add > View. Configure a View como na Figura 9.

Figura 9 - Criando a View Browse.
Figura 9 - Criando a View Browse.

Por fim, a Figura 10 ilustra como fica a View Browse.aspx.

Figura 10 - View Browse.aspx.
Figura 10 - View Browse.aspx.

O único detalhe nesta View é que como não havia um CategoryCount, utilizei o próprio Count da lista de categorias, na linha 11. Não tem nada demais, apenas intúito de demonstrar que podemos utilizar métodos das listas na View sem problemas.

Se tudo ocorreu certo sua View Browse fica como na Figura 11.

Figura 11 - View Browse.aspx rodando.
Figura 11 - View Browse.aspx rodando.

Conclusão

Neste artigo vimos a facilidade de criar um ViewModel, preenche-la em uma Action de um Controller e repassá-la para View. Vimos também que com o Entity Framework podemos construir toda parte de acesso à dados utilizando POCO facilmente, além de gerar o banco de dados automaticamente.

Vimos que o ViewModel desacopla ainda mais a View tornando fácil de modificar qualquer parte da aplicação quando necessário.

Por fim, não podemos deixar de notar que o Visual Studio é uma IDE indispensável para criações de aplicações ASP.NET MVC, oferecendo muitas facilidades. Além disso abro um parenteses em especial para o próprio framework MVC que nesta versão 2.0 provou estar estável, robusto e pronto para ser utilizado em aplicações grandes.

Até o próximo artigo.

André Baltieri

André Baltieri - Trabalha com desenvolvimento de aplicações web a mais de 7 anos, e com ASP.NET desde 2003. É líder da comunidade Inside .NET (http://www.insidedotnet.com.br/) e do projeto Learn MVC .NET (http://learn-mvc.net/). Bacharelando em Sistemas de Informação, atualmente trabalha com desenvolvimento e suporte de aplicações web em ASP.NET/C# em projetos internacionais e ministra treinamentos e consultorias sobre a plataforma .NET.