O .NET 4 vem com uma versão muito melhorada do Entity Framework (EF) -
uma biblioteca de acesso a dados que reside no namespace
System.Data.Entity.
Quando o Entity Framework foi primeiramente introduzido com o .NET 3.5
SP1, os desenvolvedores nos forneceram muitos comentários sobre coisas
que eles achavam que estavam incompletas naquele primeiro lançamento. O time de SQL fez um bom trabalho ouvindo esse retorno, e realmente focaram em aprimorar o EF que acompanha o .NET 4.
Algumas das grandes melhorias no EF4 incluem:
- Suporte a POCO: Agora você pode definir as entidades sem a necessidade de classes base ou atributos de persistência de dados
- Suporte a Lazy Loading (carregamento tardio): Agora você pode carregar sub-objetos de um modelo sob demanda, ao invés de carregá-los antecipadamente
- Suporte a N-Camadas e Auto-Rastreamento de Entidades: trata cenários em que as entidades são passadas entre camadas ou chamadas web sem estado.
- Melhor suporte para Geração de SQL e SPROC: EF4 executa um código SQL melhor, e inclui uma melhor integração com SPROCs (stored procedures - procedimentos armazenados).
- Suporte à Pluralização Automática: o EF4 inclui suporte à pluralização automática de tabelas (por exemplo Categorias -> Categoria).
- Testabilidade Melhorada: o objeto de contexto do EF4 pode agora ser mais facilmente imitado usando interfaces.
- Suporte melhorado para os Operadores LINQ: o EF4 agora oferece suporte completo para os operadores LINQ.
O Visual Studio 2010 também inclui um suporte muito mais rico para o designer e ferramentas do EF. O
designer do EF no VS 2010 suporta tanto um estilo de desenvolvimento
"database first" - "banco de dados primeiro" - onde você constrói a sua
camada do modelo de dados através do designer a partir de um banco de
dados existente. Ele também suporta
um estilo de desenvolvimento "model first" - "modelo de dados primeiro" -
onde você primeiro define o seu modelo de dados usando o designer, e
então o usa para gerar um esquema de banco de dados a partir dele.
Desenvolvimento de Código Antecipado com o EF
Além de suportar o desenvolvimento baseado em um fluxo de trabalho que utiliza o designer, o EF4 também permite uma opção mais centrada em código a qual nos chamamos de Code-First Development ou "Desenvolvimento de Código Antecipado". O Desenvolvimento de Código Antecipado permite um fluxo de trabalho de desenvolvimento muito suave. Ele permite que você:
- Desenvolva sem ter que abrir um designer ou definir um arquivo de mapeamento XM
- Defina o seu modelo de objetos, bastando escrever "classes de objetos padrão", sem a necessidade de classes base
- Use
uma abordagem "Convenção sobre Configuração", que permite a
persistência de dados sem ter que explicitamente configurar alguma coisa
- Opcionalmente,
substitua a persistência baseada em convenção passando a usar uma API
de código fluente para personalizar completamente o mapeamento da
persistência
O suporte ao desenvolvimento de código atencipado do EF é atualmente
ativado com um download separado que roda por cima do núcleo interno do
EF que acompanha o .NET 4. A CTP4 desta biblioteca foi liberada nesta semana e pode ser baixada aqui.
Ela funciona com o VS 2010, e você pode usá-la com qualquer projeto do .NET 4 (incluindo ASP.NET Web Forms e ASP.NET MVC).
Tutorial passo a passo: Construindo a Aplicação NerdDinner utilizando uma abordagem Code-First (Código-Primeiro)
Ano passado eu escrevi um tutorial para a ASP.NET MVC 1.0 que foi publicado tanto online (em Inglês) como também em um livro. O
tutorial ensinou como criar uma aplicação simples, chamada
"NerdDinner", que oferece uma maneira fácil para as pessoas organizarem,
hospedarem e coletarem respostas para convites de jantar de maneira
online. Você pode ler o meu tutorial NerdDinner original para a ASP.NET V1 aqui. Uma versão atualizada do tutorial também está incluída no novo livro Professional ASP.NET MVC 2.
O tutorial NerdDinner utilizou uma abordagem "banco de dados primeiro",
onde o esquema do banco foi definido em primeiro lugar, e em seguida,
usamos um designer do Visual Studio para criar nosso modelo de objetos
LINQ para SQL/LINQ para objetos para ele.
A seguir vou demonstrar como podemos utilizar uma abordagem "code
first" (código em primeiro lugar) usando o EF4 para construir a camada do modelo de dados
NerdDinner e o esquema do banco de dados. Construiremos também uma
aplicação CRUD usando a ASP.NET MVC.
Nós construiremos esta aplicação passo a passo. Um link para baixar a versão completa desta amostra está disponível no final deste post.
Passo 1: Criar uma Aplicação ASP.NET MVC 2 Nova e Vazia
Vamos começar criando um novo projeto ASP.NET MVC 2 no Visual Studio 2010. Escolha File ->New Project e use o modelo de projeto "ASP.NET MVC 2 Empty Web Application" para tanto.
Isso criará um projeto ASP.NET MVC 2 vazio que não tem controladores, modelos ou visões dentro dele:
Nós vamos trabalhar para definir o nosso "modelo" NerdDinner - que se
refere aos objetos que representam os dados da nossa aplicação, bem como
a lógica do domínio correspondente, que integra a validação e regras de
negócios. O modelo é o "coração" de uma aplicação baseada em MVC, e fundamentalmente, guia o comportamento dela. Nós vamos criar esta camada do modelo usando as novas capacidades de "Codificação Antecipada" do EF4.
Passo 2: Criar o nosso modelo
Vamos assumir que nós não temos um banco de dados definido, e que
estamos construindo nossa nova aplicação NerdDinner completamente a
partir do zero.
Nós não precisamos começar com um banco de dados
Quando utilizamos um fluxo de trabalho baseado em codificação antecipada, nós não precisamos começar nossa aplicação criando um banco de dados ou especificando um esquema. Ao
invés disso, podemos começar escrevendo classes padrão .NET que definem
os objetos do modelo de domínio que são mais apropriados para nossa
aplicação - sem nos preocuparmos em misturar lógica de persistência de
dados dentro dessas classes.
Criando as classes do Modelo
NerdDinner é uma pequena aplicação, e nossas necessidades de armazenamento de dados para ela são muito simples. Nós
queremos ser capazes de definir e armazenar informações de "Jantares"
(Dinners) que se referem a eventos específicos que as pessoas podem
participar. Também queremos ser
capazes de definir e armazenar confirmações "RSVP" (responda por favor), que são usadas para
controlar o interesse de uma pessoa em participar de um jantar
específico.
Vamos criar duas classes (Dinner e RSVP) para representar estes conceitos. Nós vamos fazer isso adicionando duas novas classes no nosso projeto ASP.NET MVC - "Dinner" e "RSVP":
As classes do modelo "Dinner" e "RSVP" acima são "simples objetos da CLR" (também conhecidas como POCO). Elas não
precisam derivar de qualquer classe base ou implementar interfaces, e
as propriedades que elas expõem estão no padrão de tipos de dados do
.NET. Nenhum atributo de persistência de dados ou código de dados foi adicionado a elas.
A capacidade de definir classes do modelo sem ter que amarrá-las a um
banco de dados específico, API de banco de dados, ou implementação de
esquema de banco de dados é realmente poderosa - e nos dá muito mais
flexibilidade para acessar os dados. Esta
capacidade nos permite concentrar em nossa aplicação/necessidades do
negócio sem termos que nos preocupar com a implementação da persistência
dos dados. Também nos dá a
flexibilidade para mudar o nosso esquema de banco de dados ou
implementação de armazenamento no futuro - sem a necessidade de
re-escrever os nossos objetos do modelo, ou o código que interage com
eles.
Criando uma Classe de Contexto para Manipular a Persistência do Banco de Dados
Agora que nós definimos nossas duas classes POCO do modelo, vamos criar
uma classe que podemos usar para lidar com a recuperação/persistência
de instâncias de Dinner e RSVP de um banco de dados.
Chamaremos esta classe de "NerdDinners". Ela
deriva da classe base DbContext, e publica duas propriedades públicas -
uma que expõe nossos objetos do tipo Dinner, e outra que expõe objetos
do tipo RSVP:
As classes DbContext e DbSet utilizadas acima são fornecidas como parte da biblioteca de codificação antecipada do EF4. Você precisará adicionar uma referência ao assembly System.Data.Entity.CTP que está instalado no diretório \Program Files\Microsoft ADO.NET Entity Framework Feature CTP4\Binaries para referenciar estas classes. Você também vai querer adicionar uma referência ao namespace System.Data.Entity no topo da sua classe "NerdDinners".
Esse é todo o código que precisamos escrever
As três classes acima contêm todo o código necessário para implementar um modelo básico e uma camada de persistência de dados para nossa aplicação NerdDinner. Nós não
precisamos configurar qualquer informação adicional de mapeamento do
esquema do banco de dados, nem executar qualquer ferramenta, nem editar
qualquer arquivo XML, nem utilizar qualquer designer a fim de começar a
usar nossas classes para recuperar, atualizar e salvar os dados em um
banco de dados.
Mapeamento de Persistência Baseado em Convenção
Nós não precisamos escrever nenhum código adicional, nem criar qualquer
arquivo XML, nem usar quaisquer ferramentas para mapear as nossas
classes do modelo para/e a partir de um banco de dados. Como, você pode perguntar, isso é possível?
Por padrão, a codificação antecipada do EF suporta uma abordagem
"Convenção sobre configuração" que lhe permite depender de convenções de
mapeamento comuns, em vez de ter de configurar explicitamente as
coisas. Você pode substituir essas convenções se você quiser fornecer regras de mapeamento personalizadas para o banco de dados. Mas
se você ao invés disso, utilizar as convenções padrão, você verá que a
quantidade de código que você tem que escrever é muito pequena, e 90%
dos cenários comuns "simplesmente funcionam" da maneira que você
esperaria, sem qualquer código extra ou configuração.
Em nosso exemplo acima, a nossa classe de contexto NerdDinners irá por
padrão mapear suas propriedades "Dinners" e "RSVPs" para as tabelas
"Dinners" e "RSVPs" dentro um banco de dados. Cada linha dentro da tabela Dinnners será mapeada para uma instância da nossa classe "Dinner". Da mesma forma, cada linha da tabela RSVPs será mapeada para uma instância da nossa classe "RSVP". Propriedades
dentro das classes "Dinner" e "RSVP" por sua vez serão mapeadas para as
colunas dentro das respectivas tabelas "Dinners" e "RSVPs" do banco de
dados.
Outras convenções padrão suportadas pelo EF incluem a capacidade de
identificar automaticamente as chaves primárias e chaves estrangeiras,
com base em padrões de nomenclatura comum (por exemplo: um ID ou
propriedade DinnerID na classe Dinner será inferida como a chave
primária). O EF também inclui convenções inteligentes para ligar relacionamentos de associação entre modelos. A equipe do EF tem um post que fala mais sobre como o conjunto de convenções padrão funcionam aqui (em Inglês).
Exemplos de Código de Como Usar Nosso Modelo
As três classes que criamos anteriormente contém todo o código necessário para implementar nosso modelo e persistência de dados para a aplicação NerdDinner. Vamos agora olhar alguns exemplos de código que mostram como podemos usar essas classes para executar cenários comuns de dados:
Pesquisando Dados usando Expressões LINQ
Nós podemos escrever expressões de consulta LINQ para recuperar dados de um banco de dados usando o seguinte código. As seguir nós estamos usando uma expressão LINQ para recuperar todos os jantares que ocorrem no futuro:
Nós também podemos tirar proveito das relações entre Dinners e RSVPs quando escrevemos nossas expressões LINQ. Observe a seguir como a nossa declaração "where" filtra jantares que possuem uma contagem de RSVP maior que 0:
Observe que o filtro "where" na consulta acima (onde estamos
recuperando apenas os jantares que têm pelo menos uma RSVP) é executado
no servidor de banco de dados - fazendo com que a consulta e a
quantidade de dados que recuperamos seja muito eficiente.
Recuperando uma Única Instância
Podemos usar o método Single() de LINQ com uma consulta lambda para
recuperar uma única instância de um jantar usando um código como a
seguir:
Alternativamente, também podemos tirar proveito de um método Find() que
o paradigma de codificação antecipada do EF expõe. Este método permite
que você recupere facilmente uma instância com base na sua
identificação:
Adicionando um novo Jantar
O código a seguir demonstra como criar e adicionar um novo jantar no banco de dados. Tudo
o que precisamos fazer é instanciar um "novo" objeto do tipo Dinner,
atribuir valores as suas propriedades, e, em seguida, adicioná-lo à
propriedade Dinners do nosso objeto de contexto NerdDinners. A
classe de contexto NerdDinner suporta o padrão de desenvolvimento "unity of work" (unidade
de trabalho) que permite que você adicione vários modelos no contexto, e
então chame o método "SaveChanges()" deste para persistir todas as
alterações em um banco de dados como uma única transação atômica.
Atualizando um Jantar
O código abaixo demonstra como recuperar um jantar, atualizar uma de
suas propriedades e, em seguida salvar as alterações de volta para o
banco de dados:
Passo 3: Criar um Controlador ASP.NET MVC que utiliza o nosso Modelo
Vamos agora olhar para um cenário mais completo que envolve o nosso
modelo, onde usamos uma classe de controlador para implementar a
funcionalidade necessária para publicar uma lista de jantares
programados e permitir aos usuários adicionar novos jantares:
Nós vamos implementar essa funcionalidade clicando com o botão direito
do mouse sobre a pasta "Controllers" e selecionando a opção
"Add->Controller" no menu. Vamos chamar nosso novo controlador "HomeController".
Vamos então adicionar três "métodos de ação" dentro dele que trabalham
com o modelo NerdDinners que criamos anteriormente usando o paradigma de
"codificação antecipada" do EF:
O método de ação "Index" acima recupera e processa uma lista de jantares programados.
O método de ação "Create" permite aos usuários adicionar novos jantares. O
primeiro método "Create" acima trata o cenário "HTTP GET" quando um
usuário visita a URL /Home/Create, e envia um formulário "New Dinner"
para ser preenchido. O segundo
método "Create" manipula o cenário "HTTP POST" associado ao formulário -
e trata de salvar o jantar no banco de dados. Se houver qualquer problemas de validação ele mostra novamente o formulário para o usuário com mensagens de erro apropriadas.
Adicionando Visões para nossos Controladores
Nosso próximo passo será adicionar dois "modelos de Visão" em nosso projeto - um para "Index" e outro para "Create".
Nós vamos adicionar a visão "Index" em nosso projeto movendo o cursor
do mouse para dentro do nosso método de ação Index em nosso controlador,
e, em seguida, clicaremos com o botão direito e escolheremos a opção
"Add View". Isso fará com que a janela de diálogo "Add View" apareça. Nós
vamos especificar que queremos criar uma visão fortemente tipada, e que
estaremos passando uma lista IEnumerable de objetos do tipo "Dinner" do
modelo para esta:
Ao clicarmos em "Add", o Visual Studio irá criar a visão em Views/Home/index.aspx. Vamos
então adicionar o seguinte código na visão - o qual gera uma lista
<ul> de Dinners, e renderiza um hiperlink que aponta para nosso
método de ação Create:
Iremos, em seguida, adicionar a visão "Create" em nosso projeto,
movendo o cursor do mouse para dentro do nosso método de ação Create em
nosso controlador, e, em seguida, clicaremos com o botão direito e
escolheremos a opção "Add View". Dentro
da janela de diálogo "Add View" vamos especificar que queremos criar
uma visão fortemente tipada, e que estamos passando para esta um objeto
do tipo Dinner. Também vamos indicar que queremos "descobrir automaticamente" suas propriedades usando um modelo de visão "Create":
Ao clicar no botão "Add", o Visual Studio irá criar um arquivo Views/Home/Create.aspx com algum conteúdo gerado automaticamente que cria um <form> HTML para o objeto "Dinner". Nós vamos ajustá-lo um pouco retirando o elemento de entrada para a propriedade DinnerID. O conteúdo final do nosso modelo de visão será parecido com este:
Temos agora implementado todo o código que precisamos escrever dentro
do nosso Controlador e Visões para implementar a lista de jantares e a
funcionalidade de criação de novos jantares dentro da nossa aplicação
web.
Etapa 4: O Banco de Dados
Nós escrevemos nosso código. Agora vamos executar a aplicação.
Mas e o banco de dados?
Nós ainda não temos um banco de dados - e não precisamos de um até
agora porque o nosso fluxo de trabalho de desenvolvimento que usa
"codificação antecipada" ou "código em primeiro lugar" não nos obrigou a ter um banco de dados para
definirmos e usarmos nossas classes do modelo.
Mas vamos precisar de um banco de dados quando nós realmente
executarmos nossa aplicação e quisermos armazenar nossos objetos Dinner e
RSVP. Podemos criar o banco de dados de duas maneiras:
- Nós
mesmos manualmente podermos criar e definir o esquema usando uma
ferramenta de banco de dados (por exemplo, SQL Server Management Studio
ou Visual Studio)
- Criar
e gerar o esquema automaticamente a partir do nosso modelo de classes
usando a biblioteca de codificação antecipada do EF.
Esta segunda opção é muito legal e é a que vamos usar para a nossa aplicação NerdDinner.
Configurando nossa String de Conexão do Banco de Dados
Para começar, nós configuramos uma string de conexão para apontar para onde queremos que o nosso banco de dados resida. Nós vamos fazer isso adicionando uma string de conexão chamada "NerdDinners" no arquivo web.config da nossa aplicação assim:
Por padrão, quando você cria uma classe DbContext usando a codificação
antecipada do EF, ele vai procurar por uma string de conexão que
corresponda ao nome da classe do contexto. Já
que o nome da nossa classe de contexto é "NerdDinners", ele irá
procurar e usará por padrão a string de conexão "NerdDinners" descrita
acima, quando o contexto for instânciado dentro da nossa aplicação
ASP.NET.
Tirando vantagem do SQL CE 4
Você pode usar bases de dados diferentes com a codificação antecipada do EF - incluindo o SQL Server, SQL Express e MySQL.
Há duas semanas atrás eu escrevi (em Inglês) sobre o trabalho que estamos fazendo para permitir que o engine do banco de dados SQL CE 4 funcione dentro da ASP.NET. SQL
CE 4 é um banco de dados leve e baseado em arquivos que é gratuito,
simples de configurar, e pode ser incorporado em suas aplicações
ASP.NET. Ele suporta ambientes de hospedagem de baixo custo, e permite que bancos de dados sejam facilmente migrados para o SQL Server.
O SQL CE pode ser uma opção útil para se usar quando você está nos
estágios iniciais de definição (e redefinindo) sua camada do modelo de
dados - e quer ser capaz de rapidamente criar e recriar seu banco de
dados durante estes processos. Nós vamos usar o SQL CE 4 para começar à medida em que desenvolvemos nossa aplicação NerdDinner. Podemos
mais tarde, opcionalmente, alterar a string de conexão para usar o SQL
Express ou SQL Server para instalarmos a aplicação no ambiente de
produção - sem ter que modificar uma única linha de código dentro de
nossa aplicação.
A string de conexão que eu especifiquei acima aponta para um arquivo de
banco de dados NerdDinners.sdf, e especifica o provedor de banco de
dados SQL CE 4. Para que isso funcione você precisa instalar o SQL CE 4 - quer através do Instalador SQL CE ou instalando WebMatrix (que o inclui por padrão). o banco de dados SQL CE 4 é um download pequeno que leva apenas alguns segundos para instalar.
Importante:
Na string de conexão acima eu estou indicando que queremos criar o
arquivo NerdDinners.sdf dentro da pasta |DataDirectory| - a qual em uma
aplicação ASP.NET é a pasta \App_Data\ imediatamente abaixo do diretório
da aplicação. Por padrão, o modelo de projeto da "Aplicação Web ASP.NET MVC Vazia" não cria este diretório. Você
precisará criar explicitamente esse diretório dentro de seu projeto
(clique com o botão direito do mouse sobre o projeto e escolha a opção
"Add->ASP.NET Folder>Add_Data").
Criação Automática do Esquema do Banco de Dados
O paradigma de codificação antecipada do EF suporta a capacidade de
gerar automaticamente o esquema do banco de dados criando bancos de
dados a partir de classes do modelo - o que lhe permite evitar a
execução manual destes passos.
Isso acontece por padrão se sua string de conexão apontar para qualquer arquivo de banco de dados SQL CE ou SQL Express que ainda não exista no disco. Você não precisa executar nenhum passo manual para que isto aconteça.
Para ver isso em ação, podemos pressionar F5 para executar nossa aplicação NerdDinner. Isto abrirá um browser na URL raiz "/" da nossa aplicação. Você deverá ver uma tela igual a mostrada a seguir:
A URL "/" quando solicitada para a nossa aplicação invocou o método de ação HomeController.Index() -
o qual instanciou e consultou nosso objeto de contexto NerdDinners
para recuperar todos os jantares programados a partir de nossa base de
dados. Como o arquivo do banco de
dados NerdDinners.sdf ainda não existia, a biblioteca de codificação
antecipada do EF gerou o mesmo automaticamente para nós. Esta usou nosso objeto de contexto NerdDinners para inferir o esquema do banco de dados gerado.
Para ver o arquivo do banco de dados SQL CE que foi gerado, clique no
botão "Show all Files" (Mostrar todos os arquivos) dentro do Solution
Explorer no Visual Studio, e pressione o botão "Update" (Atualizar) e
expanda a pasta App_Data:
Nós estaremos lançando uma atualização para o VS 2010, no futuro, que
lhe permitirá abrir e editar bases de dados SQL CE 4 na tab "Server
Explorer" (como você faz hoje, com banco de dados SQL). Isto permitirá que você possa facilmente ver (e, opcionalmente, ajustar) o esquema e o conteúdo do banco de dados. Até
lá, você pode opcionalmente usar as ferramentas de banco de dados
dentro da WebMatrix para examinar o conteúdo do arquivo de dados do SQL
CE 4.
Nós não especificamos nenhuma regra de mapeamento de persistência
personalizada em nosso contexto NerdDinners - assim o banco de dados que
foi gerado seguiu o padrão da codificação antecipada do EF no que diz
respeito às convenções de nomenclatura para mapear o esquema. Se
tivéssemos especificado qualquer regra de mapeamento customizada, a
biblioteca de codificação antecipada do EF teria honrado as mesmas e
geraria um banco de dados que corresponderia a tais regras.
Só para refrescar nossa memória - aqui estão as duas classes POCO do
modelo e a classe do contexto NerdDinners que definimos anteriormente:
A seguir estão as tabelas que foram adicionadas quando executamos nossa
aplicação e o banco de dados criado automaticamente com base no
modelo acima:
A definição da tabela "Dinners" é mostrada a seguir. Os nomes das colunas e os tipos de dados mapeiam para as propriedades da classe Dinner que nós definimos. A coluna DinnerID também foi configurada para ser tanto uma chave primária e uma coluna de identidade:
A definição da tabela "RSVPs" é mostrada a seguir. Os nomes das colunas e tipos de dados mapeiam para as propriedades da classe RSVP que nós definimos. A coluna RsvpID também foi configurada para ser tanto uma chave primária e uma coluna de identidade:
Um relacionamento do tipo um para muitos (chave primária/chave estrangeira) também foi estabelecido entre Dinners e RSVPs. A
biblioteca de codificação antecipada do EF inferiu que este
relacionamento deve ser estabelecido porque nossa classe Dinner tem uma
propriedade do tipo ICollection<RSVP> chamada RSVPs, e a classe
RSVP tem uma propriedade Dinner.
Preenchendo o Banco de Dados com alguns Jantares (Dinners)
Vamos agora criar e adicionar alguns jantares em nosso banco de dados. Nós
vamos fazer isso clicando no link "Create New Dinner" na nossa
home-page para navegar para o nosso formulário/visão "Create":
Quando clicamos no botão "Create", o nosso novo jantar será salvo no banco de dados. Podemos repetir este processo várias vezes para registrar vários jantares diferentes Cada
novo jantar que criarmos será mantido dentro do nosso banco de dados e
aparecerá na nossa lista de jantares programados na Home page:
Passo 5: Mudando nosso Modelo
Vamos evoluir e refatorar continuamente nosso modelo, de acordo com o crescimento de nossa aplicação. A
biblioteca code-only (apenas-código) do EF inclui alguns recursos de
desenvolvimento legais que tornam mais fácil coordenar essa evolução com
um banco de dados de desenvolvimento.
Adicionando uma nova Propriedade para o modelo Dinner
Vamos seguir passo a passo fazendo uma simples mudança
em nossa classe Dinner. Especificamente, nós vamos adicionar uma propriedade adicional em nossa classe Dinner chamada "Country" (País):
Agora que fizermos esta mudança, vamos pressionar F5 no Visual Studio para construir e re-executar a aplicação. Quando fazemos isso nós vamos ver a seguir mensagem de erro:
Esta mensagem de erro ocorre porque nós mudamos a estrutura da nossa
classe Dinner, e nosso modelo de objeto já não apresenta a mesma forma
que a tabela "Dinners" que automaticamente criamos dentro do nosso banco
de dados.
Quando o EF cria automaticamente um banco de dados para você, por
padrão ele adiciona uma tabela chamada "EdmMetadata" no banco de dados
que controla a forma dos objetos do modelo que foram usados para criar
automaticamente o esquema do banco de dados para você:
A mensagem de erro acima ocorre quando o EF detecta que você fez uma
mudança em um objeto do modelo e ele agora está fora de sincronia com o
banco de dados que foi criado automaticamente para você.
Re-sincronizando nossas Classes do Modelo com o Banco de Dados
Existem algumas maneiras pelas quais nós podemos "re-sincronizar" os objetos de nosso modelo com o banco de dados
- Podemos atualizar manualmente o nosso esquema do banco de dados para coincidir com os nossos modelos
- Podemos
excluir manualmente o nosso arquivo de banco de dados, re-executar a
aplicação, e o EF automaticamente re-criará o banco de dados
- Podemos
ativar um recurso da codificação antecipada do EF que atualiza
automaticamente o nosso banco de dados sempre que nós efetuarmos uma
mudança em os nossos modelos
Vejamos como podemos utilizar essa última opção automática com a nosso aplicação NerdDinner.
O recurso RecreateDatabaseIfModelChanges (Recriar o banco de dados se o modelo for modificado)
A CTP 4 da biblioteca de codificação antecipada do EF inclui um recurso útil no tempo de desenvolvimento que permite que você automaticamente re-crie o banco de dados quando você fizer modificações em suas classes do modelo. Quando
você ativá-lo, o EF identifica quando qualquer uma das classes do
modelo que foram usadas para criar automaticamente um banco de dados são
modificadas, e quando isso acontece ele pode recriar seu banco de dados
para que o mesmo coincida com a forma da classe - sem que você tenha que executar algum passo manualmente.
Esse recurso é especialmente útil quando você é o primeiro a
desenvolver uma aplicação, uma vez que lhe dá a liberdade e
flexibilidade para rapidamente refazer e reestruturar seu modelo de
código como quiser - sem ter que realizar qualquer trabalho manual para
manter o seu esquema do banco de dados em sincronia ao longo do
caminho. Ele funciona especialmente
bem com o SQL CE - uma vez que é um banco de dados baseado em arquivo
que pode ser descartado e recriado em tempo real em menos de um
segundo. Isso pode permitir um fluxo de trabalho de desenvolvimento extremamente fluído.
A maneira mais fácil para habilitar essa capacidade é adicionando uma chamada Database.SetInitializer() no tratador do evento Application_Start() dentro da nossa classe Global.asax:
Isto informa ao EF que ele deve recriar nossa base de dados
NerdDinners.sdf para que a mesma seja compatível com o nosso modelo
NerdDinners em qualquer momento que nossas classes do modelo mudem de
forma. Agora, quando reexecutarmos
nossa aplicação, vamos deixar de receber aquela mensagem de erro nos
informando que o nossas classes do modelo e o banco de dados estão fora
de sincronia. O EF, irá ao invés de
exibir a mensagem, automaticamente re-criar o banco de dados para nós de
forma que este seja compatível com a nova forma das nossas classes do
modelo e nossa aplicação funcionará perfeitamente:
Adicionando Dados Iniciais em Bancos de Dados Criados Automaticamente
Uma das coisas que você deve ter notado na tela acima é que nós
perdemos nossos dados de jantares quando recriamos o banco de dados. Isso
ocorre porque o comportamento automático de
"RecreateDatabaseIfModelChanges" não se destina a cenários de produção
onde você deseja "migrar" os dados existentes de um esquema para outro. Em vez disso, este recurso é
projetado para cenários de desenvolvimento onde você deseja que o banco
de dados seja rapidamente e automaticamente atualizado para você - sem
que você tenha que executar passos manuais ou especificar as regras de
migração para o fazer.
Nota:
Estamos trabalhando separadamente para fornecer melhor suporte à
migração de dados para cenários onde você está trabalhando com dados de
produção e deseja versionar o esquema do banco de dados. Nós
achamos que este cenário é diferente deste recurso existente para o
tempo de desenvolvimento que eu estou descrevendo aqui. A capacidade de migração de dados não está ativada ainda com a CTP desta semana.
O EF nos fornece a capacidade de opcionalmente "adicionar dados
iniciais" em nosso banco de dados (dados padrão/de teste) a qualquer
momento em que o banco de dados seja criado/recriado. Acho
que esse recurso é realmente útil, pois ele me permite refazer um
modelo, e então rapidamente executar a aplicação para testar um cenário -
sem que eu tenha que entrar manualmente com um monte de dados de teste
para fazê-lo.
Nós podemos "adicionar dados iniciais" em nosso banco de dados
NerdDinners com dados padrão, escrevendo uma classe
"NerdDinnersIntializer", igual a mostrada a seguir. Estou usando esta classe para criar dois "jantares de amostra" e adiciono os mesmos em nosso banco de dados da seguinte forma:
Podemos, então, atualizar a chamada Database.Initializer()
que adicionamos em nosso arquivo Global.asax para usar esta classe
"NerdDinnersInitializer" na inicialização da aplicação:
E agora a qualquer hora que fizermos uma alteração em uma das nossas
classes do modelo NerdDinner, o banco de dados será automaticamente
descartado e recriado para coincidir com os nossas classes do modelo, e
teremos dois jantares já inseridos no banco de dados para fins de teste:
Fácil Refatoração
Os recursos acima tornam realmente fácil evoluir e refatorar seu código
no tempo de desenvolvimento - sem ter que utilizar ferramentas ou
executar scripts manualmente para manter seu banco de dados em sincronia
com as mudanças de seu código.
Devido as nossas classes do modelo, expressões LINQ, e "dados iniciais"
de teste serem fortemente tipificados/tipados, também podemos tirar
proveito do suporte de ferramentas de refatoração dentro do Visual
Studio para rapidamente e automaticamente aplicar alterações em toda a
nossa base de código de uma maneira rápida e fácil.
Passo 6: Adicionando Regras Validação
Nós construímos uma simples e boa aplicação para entrada de dados.
Um problema com ela, porém, é que não temos atualmente nenhum tipo de
validação de dados de entrada em vigor para garantir que os campos sejam
preenchidos corretamente dentro da nosso formulário de Criação de
Jantar. Vamos corrigir isso.
Adicionando Validação usando DataAnnotations
Regras de validação em uma aplicação baseada em ASP.NET MVC geralmente
são expressas de uma maneira melhor dentro de um modelo. Isso
permite que elas sejam mantidas em um único lugar, e que sejam
executadas em qualquer número de controladores e visões que possam
interagir com elas. A ASP.NET MVC
permite que você implemente regras de validação usando uma variedade de mecanismos diferentes, e é flexível o suficiente para suportar
praticamente qualquer esquema de validação que você deseje usar.
A ASP.NET MVC 2 inclui suporte nativo para usar a biblioteca de regras de validação do .NET System.ComponentModel.DataAnnotations - a qual permite que você declarativamente aplique regras de validação em classes do modelo usando atributos de validação. Você pode aprender mais sobre esse recurso em um post anterior que eu escrevi . Vamos aproveitar essa abordagem para permitir a validação dos dados de entrada para a nossa aplicação NerdDinner.
Vamos voltar na classe Dinner que definimos anteriormente e vamos
adicionar alguns atributos de validação em suas propriedades (nota: é
preciso adicionar uma referência para o namespace "using System.ComponentModel.DataAnnotations" tabém):
O atributo de validação [Required] (Obrigatório) indica que uma propriedade particular deve ser especificada. O
atributo de validação [StringLength] (Comprimento da String)
permite-nos indicar um comprimento máximo de uma propriedade particular
do tipo string. O atributo de
validação [RegularExpression] (Expressão Regular) permite-nos indicar
que uma propriedade particular do tipo string deve coincidir com uma
expressão regular especificada, a fim de ser válida - neste caso, um
endereço de e-mail.
Cada um dos atributos de validação suporta uma propriedade
"ErrorMessage" (Mensagem de Erro) - a qual nos permite especificar uma
mensagem de erro que deve ser exibida se a validação falhar. Esta
mensagem pode ser inserida como uma string (como acima) ou e ser proveniente
de um arquivo de recurso - o que lhe permite localizar (traduzir) a
mensagem de erro facilmente em vários idiomas.
Referenciando alguns arquivos CSS e JavaScript
A última etapa será voltar em nosso modelo de visão Create.aspx para
adicionarmos uma um elemento <link> que referência o arquivo
Site.css no nosso projeto, bem como dois elementos <script> para
referenciar dois arquivos JavaScript no nosso projeto. Também vamos adicionar uma linha de código para chamar o método Html.EnableClientValidation() antes que o nosso elemento <form> seja processado:
Estas alterações irão garantir que as mensagens de erro de validação
que são exibidas na página sejam estilizadas (para torná-las mais
visíveis), e que as regras de validação que aplicamos em nosso modelo
sejam aplicadas tanto no cliente como servidor.
Executando a Aplicação
Vamos re-executar a aplicação e tentar criar um novo jantar. Vamos começar pressionando o botão "Create" sem valores preenchidos. Nós vemos agora as mensagens de erro de validação que aplicamos no nosso modelo aparecendo no navegador:
Devido a permitirmos a validação no lado do cliente com a ASP.NET MVC
(uma linha de código que escrevemos acima), nossas mensagens de erro
serão atualizadas e mudarão em tempo real:
Observe acima como nossa mensagem de erro de validação mudou no momento
em que nosso campo "Title" ficou maior que 20 caracteres. Isto é porque nós temos uma propriedade [StringLength] em nossa propriedade Dinner.Title que indica um tamanho máximo permitido de 20 caracteres. Quando
começamos a entrar um valor dentro do campo de texto "HostedBy", a
nossa mensagem de erro também mudou de uma mensagem de erro "[Requred]"
(que pede que você digite o seu endereço de e-mail) para uma mensagem de
erro "[RegularExpression]" (que está nos informando que não temos um
endereço de e-mail válido).
Estas regras de validação tanto funcionam tanto dentro do navegador
(via JavaScript) como no servidor (o que nos permite proteger a nós
mesmos, mesmo se alguém tentar contornar a validação JavaScript) - sem
que tenhamos que fazer quaisquer mudanças em nossa classe de
controlador. A habilidade de
especificar essas regras uma única vez dentro do nosso modelo, e tê-las
aplicadas em todos os lugares, é extremamente poderosa - e nos permitirá
continuar a evoluir a nossa aplicação de uma forma muito limpa.
Você pode aprender mais sobre estes recursos do Modelo de Validação da ASP.NET MVC e como eles funcionam aqui.
Download
Clique aqui para fazer o download e executar o exemplo de aplicação NerdDinnerReloaded que nós construímos neste post. A aplicação requer o VS 2010 (ou o Visual Web Developer 2010 Express que é gratuito).
Importante: Você deve baixar e instalar o SQL CE 4 em sua máquina para que a aplicação de exemplo acima funcione. Você pode fazer o download da biblioteca de codificação antecipada do EF aqui. Nenhum destes downloads vai impactar sua máquina.
Resumo
O lançamento CTP4 da funcionalidade "Code-First" ou "Codificação
Antecipada" do EF fornece uma maneira bastante agradável e centrada em
código para trabalhar com dados. Ele traz consigo uma série de recursos de produtividade, bem como muito poder para o desenvolvimento. No
tutorial de hoje, concentrei-me principalmente em algumas das novas
melhorias de produtividade que foram liberadas com o lançamento da
CTP4. Existem muitos outros cenários
que poderíamos explorar incluindo sua API Fluent (Fluente) que permite
regras de mapeamento de persistência personalizadas, o seu suporte
melhorado para testabilidade, e outros recursos mais avançados.
Você pode fazer o download da CTP4 do EF Code-First desta semana aqui. Para saber ainda mais sobre o desenvolvimento com "EF Code-First" verifique esses posts do time do ADO.NET (todos em Inglês):
Espero que ajude,
Scott
PS Além do blog, eu também estou agora utilizando o Twitter para atualizações rápidas e para compartilhar links. Siga-me em: twitter.com/ScottGu.
Texto traduzido do post original por
Leniel Macaferi.