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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.