Formatar dados com o ASP.NET Dynamic Data

Renato Haddad
Microsoft MVP, MCT, MCPD e MCTS.
Maio 2009
 

Tecnologias utilizadas: ASP.NET
Sumário: Neste artigo vou abordar como criar uma aplicação dinâmica de dados oriundos a partir de uma modelo de dados do Linq To SQL, aplicar uma formatação nos dados e como ocultar algumas tabelas.

arrow_px_down.gif Introdução
arrow_px_down.gif Criação do Projeto
arrow_px_down.gif Global.asax
arrow_px_down.gif Execução
arrow_px_down.gif Configurar Empregados
arrow_px_down.gif Configurar Produtos
arrow_px_down.gif Validação de Dados
arrow_px_down.gif Conclusão
arrow_px_down.gif Referências
arrow_px_down.gif Sobre o Autor

Introdução

No artigo http://msdn.microsoft.com/pt-br/library/dd560511.aspx  mostrei detalhadamente como criar uma aplicação dinâmica com o ASP.NET. Neste artigo vou mostrar os dados formatados das tabelas que estiverem inseridas no modelo de objeto relacional. Outra funcionalidade será como aplicar uma configuração para exibir ou não uma determinada tabela para o internauta.

Como sabemos, o ASP.NET Dynamic Data é a maneira mais rápida e segura de se criar uma aplicação ASP.NET baseada em um modelo de objeto relacional, seja no ADO Entity Framework ou no LINQ TO SQL. Tudo parece tão rápido, prático e dinâmico que parece mágica, mas não é. No artigo anterior você viu os detalhes dos bastidores deste tipo de aplicação, portanto, os detalhes você pode ler no respectivo artigo.

Eu vou criar uma aplicação passo a passo para que não fique nenhuma dúvida em relação a vestígios de qualquer outra aplicação. Você irá precisar do banco de dados Northwind do SQL Server, o Visual Studio .NET 2008 com o SP1 instalado.

arrow_px_up.gif Início da Página

Criação do projeto

Com o VS.NET 2008 aberto, crie um novo tipo de projeto (CTRL + SHIFT + N). Selecione a linguagem C#, o tipo de projeto Web, o template chamado Dynamic Data Web Application (Figura 1). É este o modelo a ser usado, portanto, dê um nome para a aplicação, neste caso DynamicDataFormatar e informe qual o diretório será criado.

Figura 1 – Tipo de projeto

Clique no botão OK para criar o projeto. O próximo passo é montar uma fonte de dados, então, no Solution Explorer, dê um clique com o botão direito e selecione Add New Item (Figura 2). Em Categories selecione Data e em Templates, selecione LINQ TO SQL Classes. O nome será northwind.dbml.

Figura 2 – Modelo de objeto relacional

Dê um clique no botão Add para criar o arquivo em branco. No Server Explorer já tenho uma conexão criada com o banco de dados Northwind. Caso você não tenha, basta criar uma nova conexão e apontar para o Northwind. Selecione as tabelas Categories, Employees e Products (Figura 3) e arraste-as para dentro do arquivo .dbml criado.

Figura 3 – Tabelas selecionadas no banco de dados

Veja na Figura 4 arquivo dbml como ficaram as tabelas, só que agora chamamos de classes e propriedades, mostrando todas as colunas (propriedades) e o relacionamento entre as mesmas, se houver.

Figura 4 – Classes do modelo de objeto relacional de dados

Salve este modelo (CTRL + S) e note no Solution Explorer (Figura 5) que o Northwind.dbml contém dois outros arquivos, sendo um de layout e outro com os códigos da classe em si. O melhor de tudo é que o próprio VS.NET 2008 já gera a classe Northwind.designer.cs com todos os códigos prontos. Isto é feito quando arrastamos as tabelas da fonte de dados para o arquivo .dbml. O que acontece se mudar algo na estrutura das tabelas no banco de dados? Como faço para atualizar o modelo? Abra o .dbml, exclua a respectiva tabela e arraste-a novamente do banco de dados. O código será regerado automaticamente.

Figura 5 – DBML criado no Solution Explorer

arrow_px_up.gif Início da Página

Global.asax

O arquivo Global.asax tem um papel fundamental neste projeto, sem ele nada funcionará. No Globla.asax você precisa informar qual é o nome do arquivo que contém todo o modelo de dados relacional, neste caso, o Northwind. Portanto, abra o global.asax, localize o código abaixo que está comentado. Na última linha deste bloco você irá descomentar a linha e inserir o nome do arquivo que contém o contexto, neste caso é o NorthwindDataContext. Além disto, a segunda coisa mais importante é configurar o ScaffoldAllTables = True, fazendo com que todas as classes/tabelas inseridas no modelo sejam exibidas no menu e passíveis de manutenção de dados.

//                    IMPORTANT: DATA MODEL REGISTRATION
// Uncomment this line to register LINQ to SQL classes or an ADO.NET Entity Data
// model for ASP.NET Dynamic Data. Set ScaffoldAllTables = true only if you are sure
// that you want all tables in the data model to support a scaffold (i.e. templates)
// view. To control scaffolding for individual tables, create a partial class for
// the table and apply the [Scaffold(true)] attribute to the partial class.
// Note: Make sure that you change "YourDataContextType" to the name of the data context
// class in your application.
model.RegisterContext(typeof(NorthwindDataContext), new ContextConfiguration() { ScaffoldAllTables = true });

Você deve estar pensando de onde vem o nome NorthwindDataContext? Se você abrir o arquivo Northwind.designer.cs irá notar que o nome da classe é NorthwindDataContext que herda de DataContext. Portanto, se você trocou para outro nome ou pegou outro banco de dados, atente para este nome do contexto.

public partial class NorthwindDataContext : System.Data.Linq.DataContext

arrow_px_up.gif  Início da Página

Execução

Compile o projeto (CTRL + SHIFT + B) e execute-o no browser (F5). Conforme a Figura 6 temos na página principal do site são mostradas as três classes existentes no modelo de objeto relacional (DBML).

Figura 6 – Página default do site

Se você clicar em Categories será mostrada a página dinâmica que lista todas as categorias os links para edição dos dados, conforme a Figura 7.

Figura 7 – Página de categorias

arrow_px_up.gif Início da Página

Configurar Empregados

Na página de listagem dos empregados, são mostradas todas as linhas da tabela de empregados (Figura 8). No entanto, perceba que as datas das colunas BirthDate e HireDate estão com um formato errado, afinal, basta exibir a data e não a data e hora.

Figura 8 – Dados dos empregados

Então, a solução é customizar o formato das colunas. Para isto, no Solution Explorer, adicione um novo item do tipo Class chamado Employee. Mas, espera um pouco, quem é Employee? Se você abrir o arquivo Northwind.designer.cs irá verificar que Employee já existe, afinal é o objeto oriundo do DBML, conforme o código a seguir.

public partial class Employee : INotifyPropertyChanging, INotifyPropertyChanged

Correto, mas o truque é exatamente este, nunca, jamais altere nada no DBML porque caso haja alguma manutenção nas tabelas, este arquivo será regerado e você perde tudo. Então, como a classe Employee é do tipo partial class, nada mais justo que estender a classe. Este é um dos fantásticos recursos do .NET Framework 3.5. Voltando ao arquivo Employee que você adicionou, o primeiro passo é alterar a declaração para partial class, ficando:

public partial class Employee

Em seguida, antes desta declaração acima, adicione a seguinte linha para definir o metadados que iremos usar da classe. Será preciso adicionar o using System.ComponentModel.DataAnnotations; na lista de using, mas nada que um CTRL + . (ponto) não resolva rapidamente.

[MetadataType(typeof(EmployeeMetadata))]

O construtor da classe não terá nada, pois estamos apenas estendendo a classe. Então, adicione o seguinte bloco de código logo abaixo do construtor. Se preciso, adicione o using System.ComponentModel; na lista de using.

public class EmployeeMetadata
{
    public object EmployeeID { get; set; }
    [DisplayName("Nome")]
    public object FirstName { get; set; }
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
    [DisplayName("Nascimento")]
    public object BirthDate { get; set; }
    [DisplayFormat(DataFormatString = "{0:D}")]
    [DisplayName("Admissão")]
    public object HireDate { get; set; }
}

Neste código vamos destacar o DisplayName e o DisplayFormat, ou seja, o primeiro permite você trocar o nome do label (coluna ou label) para o texto declarado, neste caso, Nome, Nascimento e Admissão. Já o segundo permite aplicar uma formatação de acordo com o tipo de dado, neste caso o "{0:dd/MM/yyyy}" mostrará a data com o dia/mês/ano e o "{0:D}" mostrará a data completa. Para efeito de visualização, veja o código completo da classe Employee estendida.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;

namespace DynamicDataFormatar
{
    [MetadataType(typeof(EmployeeMetadata))]
    public partial class Employee
    {
    }

    public class EmployeeMetadata
    {
        public object EmployeeID { get; set; }
        [DisplayName("Nome")]
        public object FirstName { get; set; }
        [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
        [DisplayName("Nascimento")]
        public object BirthDate { get; set; }
        [DisplayFormat(DataFormatString = "{0:D}")]
        [DisplayName("Admissão")]
        public object HireDate { get; set; }
    }
}

É importante você entender o uso do partial class. Isto permite ter diversos arquivos com o mesmo nome, com funcionalidades definidas em tópicos, mas a única condição é que seja do tipo partial class. Salve o projeto, compile e execute-o no browser. Na Figura 9 veja como estão os dados das colunas FisrtName (Nome), BirthDate (Nascimento) e HireDate (Admissão).

Figura 9 – Dados formatados

arrow_px_up.gif Início da Página

Configurar Produtos

Na configuração de produtos vamos aprender novos atributos, mas a idéia de classe estendida é a mesma. Portanto, adicione um novo item do tipo Class chamado Product com o seguinte código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace DynamicDataFormatar
{
    [MetadataType(typeof(ProductMetadata))]
    public partial class Product
    {
    }

    public class ProductMetadata
    {
        public object ProductID { get; set; }
        public object ProductName { get; set; }
        [Range(0, 280, ErrorMessage = "O preço deverá ser entre 0 e 280")]
        [DisplayFormat(DataFormatString = "{0:n2}")]
        public object UnitPrice { get; set; }
        [Range(0, 1500, ErrorMessage = "A qtde deverá ser entre 0 e 1500")]
        [DisplayFormat(DataFormatString = "{0:n0}")]
        public object UnitsInStock { get; set; }
        [ScaffoldColumn(false)]
        public object ReorderLevel { get; set; }
        [ScaffoldColumn(false)]
        public object Discontinued { get; set; }
        [ScaffoldColumn(false)]
        public object UnitsOnOrder { get; set; }
        [ScaffoldColumn(false)]
        public object SupplierID { get; set; }
    }
}

 Aqui temos o DisplayFormat "{0:n0}" para a propriedade UnitPrice para exibir o valor no formato numérico com zero casas decimais. O atributo Range para definir uma faixa de dados permitidos quando o internauta for alterar o preço (UnitPrice), neste caso, o valor deverá estar entre 0 e 280, senão, já mostra a mensagem de erro.

[Range(0, 280, ErrorMessage = "O preço deverá ser entre 0 e 280")]

E por fim, o atributo ScaffoldColumn (false ou true) faz com que a propriedade seja ou não exibida no formulário. Salve e execute para visualizar o resultado. Na Figura 10 são exibidas todas as propriedades dos produtos, exceto as que estão como ScaffoldColumn(false).

Figura 10 – Lista de produtos

Clique em Edit para mostrar o formulário de edição de dados. Digite o valor de 320 no campo UnitPrice e pressione TAB. Veja na Figura 11 que a mensagem de erro já é exibida porque a validação já foi aplicada à propriedade.

Figura 11 – Erro na validação

arrow_px_up.gif Início da Página

Validação de Dados

Existe outro tipo de validação de dados que podemos aplicar. No arquivo Northwind.designer.cs existe o objeto Product com diversos métodos do tipo partial. Isto significa que você pode estender um método também, não somente uma classe. Portanto, no arquivo Product.cs adicione o seguinte código no evento OnProductNameChanging para validar se o produto inicia com o texto DYN.

public partial class Product
{
    partial void OnProductNameChanging(string value)
    {
        if (!value.StartsWith("DYN"))
        {
            throw new ValidationException("O nome deverá iniciar com DYN");
        }
    }
}

Execute o projeto, tente alterar o nome de algum produto de forma que o texto não inicie com DYN. Clique no botão Update para salvar. Note que a mensagem de erro já é exibida (Figura 12) porque a condição não foi satisfeita.

Figura 12 – Validação de campo

Para finalizar este artigo, imagine que o modelo de objeto relacional tivesse uma série de classes onde nem todas deverão ser exibida ao internauta no menu para manutenção de dados. O que fazer para ocultar que toda e qualquer referência a tal classe seja oculta no projeto? Isto é simples, basta você estender a classe e inserir o código [ScaffoldTable(false)] no início da classe.

arrow_px_up.gif Início da Página

Conclusão

Customizar a exibição e a validação de dados com o Dynamic Data é um recurso simples, basta você dominar o contexto e os atributos, assim como saber montar um modelo de objetos relacionais e usar o LINQ para usufruir dos melhores recursos do Dynamic Data.

Referências

Este é o link http://www.asp.net/dynamicdata/ do time de Dynamic Data.

Sobre o Autor

Renato Haddad (rehaddad@msn.com  – www.renatohaddad.com ) é MVP, MCT, MCPD e MCTS, palestrante em eventos da Microsoft em diversos países, ministra treinamentos focados em produtividade com o VS.NET 2008, ASP.NET 3.5, LINQ, Reporting Services e Windows Mobile. Visite o blog http://weblogs.asp.net/renatohaddad . Renato é autor do livro LINQ e C# 3.0 – A Solução em Consultas para Desenvolvedores http://www.editoraerica.com.br/buscafinal.asp?cod=2366

arrow_px_up.gif  Início da Página

Mostrar: