.NET

15 dez, 2014

Code First Entity Framework com banco existente no ASP.NET MVC 5

Publicidade

Quem já ouviu falar em engenharia reversa com o Entity Framework sabe que essa é a maneira de abstrair classes em C# ou VB.NET a partir de um banco de dados existente. Isto facilita muito a vida do desenvolvedor, pois todas as entidades existentes no banco de dados são lidas e depois são geradas as devidas classes no seu projeto. Para isto, antes do Update 2 do Visual Studio, era preciso instalar a ferramenta EF Power Tools disponível na galeria do Visual Studio ou através do Nuget. Uma desvantagem do EF Power Tools é que todas as entidades e views no banco geram classes, e nem sempre queremos todos os itens e, sim, apenas alguns selecionados.

No entanto, quando foi lançado o Update 2 do Visual Studio 2013, foram adicionados novos templates para esta funcionalidade e o objetivo deste artigo é mostrar passo a passo, como ler um banco de dados existente e gerar as classes com o contexto e as telas de CRUD – claro que dentro de um projeto ASP.NET MVC 5.

Portanto, o pré-requisito para este artigo é o Update 2 do Visual Studio 2013 – se já tiver instalado os Updates 3 ou 4, melhor ainda!

Projeto ASP.NET MVC 5

Abra o VS 2013, selecione File/ New Project (Ctrl + Shift + N). Na janela aberta, selecione a linguagem Visual C#, a categoria web e nos templates, ASP.NET Web Application. O nome do projeto será ArtigoCodeFirstMVC5, conforme a figura 1.

Figura 1 – Novo projeto Web
Figura 1 – Novo projeto Web

Clique no botão OK, e na janela aberta, selecione o template MVC, conforme figura 2. Clique no botão Change Authentication e selecione No Authentication, pois não iremos usar autenticação neste projeto.

Figura 2 – Template do projeto MVC
Figura 2 – Template do projeto MVC

Gerar o modelo de dados

Clique no botão OK e aguarde o VS criar o projeto. O próximo passo é abstrair as classes de um banco de dados existente, portanto, no modelo do MVC temos a pasta Models para isto. Claro que se você tiver um projeto de Class Library somente para acesso a dados, deverá fazer isto nele e depois referenciá-lo no MVC 5.

Como o foco deste artigo é mostrar o Code First, farei neste mesmo projeto. Na pasta Models, clique com o botão direito e selecione Add / New Item. Nos templates, selecione Visual C# / Data / ADO.NET Entity Data Model. No nome do modelo, digite ModeloDados, conforme a figura 3. Aqui vai uma dica para quem usa o .EDMX, no Visual Studio .NET 2015, ele deixará de existir, portanto, aprenda esta técnica deste artigo.

Figura 3 – Template do Modelo de dados
Figura 3 – Template do Modelo de dados

Clique no botão Add. Note que há quatro templates: dois para Designer e dois para Code First; ambos para um banco existente (from Database) ou vazio (Empty). O “from Database” sempre será gerado a partir de um banco existente e o Empty gera uma estrutura vazia – o que muitas vezes nos auxilia na criação quando ainda não se sabe a ideal estrutura das classes para gerar o banco futuramente. Neste exemplo, selecione “Code First from database”, conforme a figura 4.

Figura 4 – Template do Code First
Figura 4 – Template do Code First

Como é um assistente, clique no botão Next para o próximo passo. Aqui você deverá informar o nome do servidor (IP, localhost, local, nome do servidor, nome do computador, (localdb/v11), enfim, o nome do servidor), e qual o banco de dados. Veja na figura 5 que selecionei o banco chamado Escola, que está no meu servidor. Nem preciso dizer que se houver login, forneça, certo?! E como é uma aplicação ASP.NET, veja que a string de conexão será armazenada no arquivo Web.Config.

Figura 5 – Banco de dados selecionado
Figura 5 – Banco de dados selecionado

Clique no botão Next e o assistente se encarrega de listar todos os objetos (tables e views). Neste exemplo do meu banco de dados, figura 6, note que selecionei duas entidades. E, como não tenho nenhuma View, nada é mostrado. Sugiro você fazer um exemplo com um banco de dados que tenha Views e verá que ao final é gerada uma classe com o mesmo nome da View.

E o que significa a entidade _MigrationHistory? Como neste banco de dados usei o fantástico Migrations, esta entidade é criada automaticamente quando usei o comando Enable-Migrations / Update-Database.

Figura 6 – Entidades selecionadas
Figura 6 – Entidades selecionadas

Para finalizar o assistente, clique no botão Finish e aguarde a criação das classes. Você nem precisa se preocupar, que este processo já adiciona as referências necessárias na pasta References. Abra a pasta Models e veja que foram criadas três classes, sendo: Aluno, Professor e ModeloDados.

Primeiramente, vamos analisar o ModeloDados.cs. Aqui você nota que a classe ModeloDados herda de DbContext. Só isto já vale uma economia de muitas linhas de códigos, pois tudo o que diz respeito ao banco em si, é responsabilidade do DbContext, o qual cria, exclui, verifica se existe o banco, dentre outras coisas. E, claro que para aprender mais sobre uma classe, pressione F12 sobre a classe e explore as funcionalidades.

Em seguida, no construtor da classe, nota-se que há uma referência ao “name=ModeloDados”, o qual é o nome da chave que está no bloco <ConnectionString> no arquivo Web.Config. Esta é a chave que aponta para o banco de dados em si, ou seja, que contém toda a string de conexão.

Depois são criadas as propriedades para referenciar as classes que foram abstraídas no banco, sendo Aluno e Professor. Observe que ambas são do tipo DbSet<TEntity>, onde TEntity é o nome da classe. O DbSet é a melhor coisa que inventaram, pois todo o CRUD é gerado dinamicamente no DbSet. Ou seja, todas as instruções de manipulações de dados no banco (Insert, Update, Delete e Select) são criadas em tempo de execução. Costumo brincar nas palestras que no compilador quem faz isto são os indianos.

C#

namespace ArtigoCodeFirstMVC5.Models
{
    using System;
    using System.Data.Entity;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq;

    public partial class ModeloDados : DbContext
    {
        public ModeloDados()
            : base("name=ModeloDados")
        {
        }

        public virtual DbSet<Aluno> Aluno { get; set; }
        public virtual DbSet<Professor> Professor { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
        }
    }
}

Veja a estrutura da classe Professor com todas as referências e uso do DataAnnotations como campo requerido (Requerid), tamanho do campo (StringLength), e ao final uma referência à propriedade Aluno que é do tipo ICollection<Aluno>. Observe que a declaração do “virtual” faz com que haja uma navegação entre as classes Professor e Aluno, ou seja, um professor tem uma coleção de alunos.

Como que estas classes são geradas? Tudo no Visual Studio é gerado a partir de um template escrito em DSL (Domain Specific Language), ou seja é um gerador de código escrito em C# ou VB.NET. Você pode escrever o seu próprio gerador (é um arquivo do tipo text template (.tt)) de códigos de acordo com a sua necessidade ou padrão da empresa.

O diretório padrão dos templates (*.tt) é o C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ItemTemplates\CSharp\Data\1028\DbCtxCSEF6, mas é claro que depende da linguagem e da funcionalidade a ser criada.

C#

namespace ArtigoCodeFirstMVC5.Models
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    [Table("Professor")]
    public partial class Professor
    {
        public Professor()
        {
            Aluno = new HashSet<Aluno>();
        }

        public int ProfessorID { get; set; }

        [Required]
        [StringLength(200)]
        public string Nome { get; set; }

        public string Telefone { get; set; }

        public decimal? Salario { get; set; }

        public string TwitterBlog { get; set; }

        public string Materia { get; set; }

        public bool Disponivel { get; set; }

        public DateTime? Admissao { get; set; }

        public virtual ICollection<Aluno> Aluno { get; set; }
    }
}

Veja a estrutura da classe Aluno que foi gerada. Note que há a navegação entre as classes através da propriedade virtual Professor. Já que é um artigo, vale uma dica do Entity Framework 6.1.1 em relação ao nome da chave primária: o EF entende como chave primária automaticamente se a propriedade se chamar ID ou NomeDaClasseID (AlunoID, ProfessorID). Assim, dispensa o uso do atributo [key] do DataAnnotations ou o PrimaryKey do FluentAPI.

C#

namespace ArtigoCodeFirstMVC5.Models
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    [Table("Aluno")]
    public partial class Aluno
    {
        public int AlunoID { get; set; }

        public int ProfessorID { get; set; }

        [Required]
        public string NomeAluno { get; set; }

        public string Email { get; set; }

        public int Ano { get; set; }

        public DateTime Inscricao { get; set; }

        public virtual Professor Professor { get; set; }
    }
}

Criar os controllers e as views

Pronto! Classes e contexto criados, agora o próximo passo é gerar os controllers e as views do projeto. Antes de mais nada e obrigatoriamente, compile o projeto (F6 ou CTRL + SHIFT + B).

Na pasta Controllers, clique com o botão direito e selecione Add / Controller. Conforme a figura 7, selecione a opção “MVC 5 controller with views, using Entity Framework”, ou seja, como temos o DbContext e as classes, vamos basear o template nelas.

Figura 7 – Gerar o Controller
Figura 7 – Gerar o Controller

Clique no botão Add e conforme a figura 8, selecione a classe Professor em “Model class”, ModeloDados em “Data context class”, selecione o checkbox “Use async controller actions” para gerar todas as Actions como assíncronas. O checkbox “Generate views” irá gerar todas as telas do CRUD completas de acordo com o template do MVC 5. Altere o nome do Controller para ProfessorController, as vezes o VS sugeri o nome da classe no plural e não quero isto.

Figura 8 – Controller de Professor
Figura 8 – Controller de Professor

Clique no botão Add e aguarde o VS gerar todo o Controller (Controllers / ProfessorController.cs) e as Views (veja na pasta Views / Professor), respectivamente. Veja a seguir, parte dos blocos de códigos da classe gerada, afinal não é o foco deste artigo analisar estas classes.

C#

public class ProfessorController : Controller
{
    private ModeloDados db = new ModeloDados();

    // GET: Professor
    public async Task<ActionResult> Index()
    {
        return View(await db.Professor.ToListAsync());
    }

    // GET: Professor/Details/5
    public async Task<ActionResult> Details(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Professor professor = await db.Professor.FindAsync(id);
        if (professor == null)
        {
            return HttpNotFound();
        }
        return View(professor);
    }

...

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Create([Bind(Include = "ProfessorID,Nome,Telefone,Salario,TwitterBlog,Materia,Disponivel,Admissao")] Professor professor)
    {
        if (ModelState.IsValid)
        {
            db.Professor.Add(professor);
            await db.SaveChangesAsync();
            return RedirectToAction("Index");
        }

        return View(professor);
    }

    // GET: Professor/Edit/5
    public async Task<ActionResult> Edit(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Professor professor = await db.Professor.FindAsync(id);
        if (professor == null)
        {
            return HttpNotFound();
        }
        return View(professor);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Edit([Bind(Include = "ProfessorID,Nome,Telefone,Salario,TwitterBlog,Materia,Disponivel,Admissao")] Professor professor)
    {
        if (ModelState.IsValid)
        {
            db.Entry(professor).State = EntityState.Modified;
            await db.SaveChangesAsync();
            return RedirectToAction("Index");
        }
        return View(professor);
    }

    // GET: Professor/Delete/5
    public async Task<ActionResult> Delete(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
...
    }

    // POST: Professor/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> DeleteConfirmed(int id)
    {
        Professor professor = await db.Professor.FindAsync(id);
        db.Professor.Remove(professor);
        await db.SaveChangesAsync();
        return RedirectToAction("Index");
    }

}

Repita o mesmo passo para a classe Aluno, conforme figura 9.

Figura 9 – Controller de Aluno
Figura 9 – Controller de Aluno

Compile o projeto novamente, e para fecharmos o artigo com chave de ouro, abra o arquivo /Views/Shared/_Layout.cshtml e adicione duas linhas na lista de opções do menu para abrirem respectivamente as páginas Index dos Controllers Professor e Aluno.

C#

<div class="navbar-collapse collapse">
    <ul class="nav navbar-nav">
        <li>@Html.ActionLink("Home", "Index", "Home")</li>
        <li>@Html.ActionLink("Professor", "Index", "Professor")</li>
        <li>@Html.ActionLink("Aluno", "Index", "Aluno")</li>
        <li>@Html.ActionLink("About", "About", "Home")</li>
        <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
    </ul>
</div>

Executar a aplicação

Compile novamente o projeto e pressione F5 para executá-la no navegador. Selecione o menu Professor e veja a lista de professores cadastrados, conforme a figura 10.

Figura 10 – Execução da aplicação
Figura 10 – Execução da aplicação

Conclusão

Produtividade é um tema constante nas discussões dos times de desenvolvimento. Para quem está usando o Entity Framework 6.1.1, vale a pena efetuar testes, escrever códigos para melhorar a performance, verificar os logs gerados no acesso a dados e, é claro, estar atento aos templates gerados, como os deste artigo. Quando pego um projeto para desenvolver ou ajudar um time, sempre uso este recurso de gerar classes a partir do banco existente.

Agradeço a oportunidade de poder compartilhar o conhecimento deste artigo. Qualquer dúvida e preparação de times de desenvolvimento, por favor me contate.