26
Criação de uma aplicação Web ASP.NET MVC5 usando Code First ASP.NET MVC é um framework para desenvolvimento de aplicações web que usa os padrões MVC (Model View Controller) e Convention over Configuration. O padrão MVC aplicado a aplicações web conduz a uma separação de responsabilidades. Esta separação explícita de responsabilidades adiciona uma pequena quantidade de complexidade extra ao projeto de uma aplicação, mas os benefícios compensam o esforço extra. Modelos: são classes que representam o domínio. Os objetos do domínio encapsulam os dados armazenados numa base de dados assim como o código usado para manipular esses dados e para aplicar lógica de negócio específica do domínio. Incluem a camada de acesso a dados (Data Access Layer – DAL) podendo usar uma ferramenta como Entity Framework. Vistas: são templates para gerar dinamicamente HTML. Controladores: classes que respondem às interacções do utilizador, acedem ao Modelo e decidem que Vista usar. Em Asp.Net estas classes têm o sufixo Controller. O framework ASPNET MVC usa convenções sobre configurações (Convention over Configuration), o que permite fazer assunções baseadas nos nomes dos diretórios. O uso de convenções reduz a quantidade de código que é necessário escrever, e também facilita a compreensão do projeto por outros desenvolvedores. Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File > New > Project… > Visual C#, Web > .Net framework 4.6 , ASP.NET Web Application Name: MvcApplication1 ASP.NET 4.6 Templates: MVC – contém o princípio de uma aplicação Web MVC, incluindo funções de gestão de contas. É possível correr a aplicação logo após ser criada. 1. Controladores Os controladores respondem aos pedidos HTTP e retornam informação para o browser. Recebem os dados de entrada, muitas vezes efetuam mudanças no modelo e fornecem dados para a vista. São responsáveis pelo fluxo da aplicação. Nos frameworks Web tradicionais um URL era mapeado num ficheiro do disco do servidor web. Por exemplo um pedido feito com o URL “/Produtos.aspx” ou “/Produtos.php” é processado pelos ficheiros “Produtos.aspx” ou “Produtos.php”. Em MVC o mecanismo de routing mapeia um URL num método de uma classe Controller. O mecanismo de routing decide que classe Controller instanciar, que método de ação invocar, e fornece os argumentos necessários a esse método. O método de ação do controlador decide que vista usar e a vista gera o HTML.

Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

Criação de uma aplicação Web ASP.NET MVC5 usando Code First 

ASP.NET MVC é um framework para desenvolvimento de aplicações web que usa os padrões 

MVC (Model View Controller) e Convention over Configuration. 

O padrão MVC aplicado a aplicações web conduz a uma separação de responsabilidades. Esta 

separação explícita de responsabilidades adiciona uma pequena quantidade de complexidade 

extra ao projeto de uma aplicação, mas os benefícios compensam o esforço extra.  

Modelos: são classes que representam o domínio. Os objetos do domínio encapsulam os 

dados armazenados numa base de dados assim como o código usado para manipular esses 

dados e para aplicar lógica de negócio específica do domínio. Incluem a camada de acesso a 

dados (Data Access Layer – DAL) podendo usar uma ferramenta como Entity Framework. 

Vistas: são templates para gerar dinamicamente HTML. 

Controladores: classes que respondem às interacções do utilizador, acedem ao Modelo e 

decidem que Vista usar. Em Asp.Net estas classes têm o sufixo Controller. 

O framework ASP‐NET MVC usa convenções sobre configurações (Convention over 

Configuration), o que permite fazer assunções baseadas nos nomes dos diretórios. O uso de 

convenções reduz a quantidade de código que é necessário escrever, e também facilita a 

compreensão do projeto por outros desenvolvedores. 

 

Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 

File  >  New >  Project…  >  Visual C#, Web >   

.Net framework 4.6 ,     ASP.NET Web Application 

Name:  MvcApplication1 

ASP.NET 4.6 Templates: MVC – contém o princípio de uma aplicação Web MVC, incluindo 

funções de gestão de contas. É possível correr a aplicação logo após ser criada. 

1. Controladores 

Os controladores respondem aos pedidos HTTP e retornam informação para o browser. 

Recebem os dados de entrada, muitas vezes efetuam mudanças no modelo e fornecem dados 

para a vista. São responsáveis pelo fluxo da aplicação. 

Nos frameworks Web tradicionais um URL era mapeado num ficheiro do disco do servidor 

web. Por exemplo um pedido feito com o URL “/Produtos.aspx” ou “/Produtos.php” é 

processado pelos ficheiros  “Produtos.aspx” ou “Produtos.php”. Em MVC o mecanismo de 

routing mapeia um URL num método de uma classe Controller. O mecanismo de routing 

decide que classe Controller instanciar, que método de ação invocar, e fornece os argumentos 

necessários a esse método. O método de ação do controlador decide que vista usar e a vista 

gera o HTML. 

Page 2: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

Asp.Net MVC implementa a variante Front Controller do padrão MVC. 

 

1. Models  >  Adicionar classe 

Models  >  botão direito do rato:  Add  >   Class…   >        Name:  Livro.cs   Add 

Colocar  propriedades dentro da classe Livro, que representam um livro na base de dados. 

 namespace MvcApplication1.Models {     public class Livro     {         public int LivroId { get; set; }         public string Titulo { get; set; }     } } 

 

 

2. Criar subclasse de DbContext 

 Entity Framework (EF) cria automaticamente a base de dados. A API de acesso a dados que foi desenvolvida para o Code First baseia‐se na classe DbContext. Para o EF coordenar as funcionalidades de gerir os acessos à base de dados, para um dado modelo de dados, temos de criar uma classe derivada da classe DbContext. Nesta classe especificámos que entidades estão incluídas no nosso modelo de dados. Também se podem configurar certos comportamentos do Entity Framework.  Criar uma pasta DAL. Dentro desta pasta criar a classe BibliotecaDbContext derivada de DbContext, que representa o contexto da base de dados, e trata de retribuir (select), guardar (insert), atualizar (update), e apagar (delete) instâncias da classe Livro na base de dados.  MvcApplication1  >  botão direito do rato:  Add  >   New Folder   >   DAL  DAL  >  botão direito do rato:  Add  >   Class…   >  Name:  BibliotecaDbContext.cs    Add 

 Acrescentar  using System.Data.Entity; para referenciar DbContext e DbSet. Acrescentar  using MvcApplication1.Models; para referenciar a classe Livro.  

using System.Data.Entity; using WebApplication1.Models;  namespace WebApplication1.DAL {     public class BibliotecaDbContext : DbContext     {         public BibliotecaDbContext() : base("DefaultConnection") { }         public DbSet<Livro> Livros { get; set; }     } } 

Page 3: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

Este código cria uma propriedade para cada entity set. No EF, um entity set tipicamente corresponde a uma tabela da base de dados, e uma entidade corresponde a uma linha da tabela.  Por omissão Entity Framework usa a SqlConnectionFactory definida na secção de configuração do Entity Framework no web.config, que aponta para o .\SQLEXPRESS. Para usar localDb em vez de SQLExpress colocámos um construtor na subclasse de DbContext especificando a connectionString  ou o nome da connectionString.  Podemos passar a connectionString ao construtor de DbContext: public BibliotecaDbContext() : base(@="Data Source=...") { }  Ou passar o nome da connectionString colocada no web.config da aplicação: public BibliotecaDbContext() : base("DefaultConnection") { }  Se não se passa qualquer argumento ao construtor de DbContext, Entity Framework procurará uma connectionString com o nome da classe DbContext criada (BibliotecaDbContext), no web.config da aplicação.  Se o Entity Framework não encontra a connectionString no ficheiro web.config, assume que "BibliotecaDbContext" é o nome da base de dados no servidor local Sql Express ou numa instância LocalDb, dependendo da connection factory  configurada.  

O ficheiro Web.config criado automaticamente para esta aplicação, tem definida uma Connection String ("DefaultConnection") para a gestão de utilizadores. Assim usando a mesma connectionString evitámos criar uma segunda base de dados.  

3. Build 

 

4. Scaffolding para criar Controladores e Vistas   

Scaffolding é usado por muitas tecnologias de software para significar geração rápida de um esquema do software que depois se pode editar. O template que gera este esquema inicial do código é usado como um andaime (scaffold) a partir do qual se constrói um programa mais poderoso.

 Controllers  >  botão direito do rato:  Add  >   Controller…   >   Controller name:    LivrosController Scaffolding options:   MVC 5 controller with views, using Entity Framework Model class:     Livro (MvcApplication1.Models) Data context class:  LivrosDbContext (MvcApplication1.DAL) Add 

 

 

Page 4: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

5. Executar a aplicação (Start without Debugging = Ctrl+F5) 

 Na barra de endereços do browser acrescentar /Livros    localhost:xxxx/Livros  A base de dados é criada pelo Entity Framework. Testar o CRUD (Create, Read, Update, Delete) da aplicação. 

 

6. Inspecionar a base de dados criada automaticamente 

 Mudar da janela Solution Explorer para a janela Server Explorer. Expandir a base de dados   DefaultConnection (MvcApplication1) debaixo de Data Connections. Expandir Tables para ver a tabela que foi criada. Na tabela > Show Table Data   

 

2. Vistas 

A primeira impressão que um utilizador tem de uma Aplicação Web depende da vista e toda a 

interacção com a aplicação é realizada através de vistas. Os controladores e os modelos são 

muito importantes mas esse trabalho não é visível. 

A vista é responsável por construir a interface com o utilizador (UI). Recebe o modelo (a 

informação que o controlador necessita de mostrar) e a vista transforma esse modelo num 

formato próprio para ser apresentado ao utilizador. 

As vistas não são diretamente acessíveis pelo browser. Uma vista é sempre invocada pelo 

controlador que lhe fornece os dados.  

 

Convenções em Asp.Net MVC 

As aplicações Asp.Net MVC, por omissão, funcionam com base em convenções. Isto evita ter 

de especificar tudo o que pode ser inferido através de convenções. 

MVC usa uma estrutura de nomes de diretórios baseados em convenções que permite 

determinar os templates das vistas referidos pelos controladores sem especificar a localização. 

O ficheiro com o template da vista referido num método de um controlador encontra‐se no 

diretório \Views\<Nome_do_Controlador>\ e tem o nome igual ao método do qual é 

invocado. 

As convenções do MVC podem ser substituídas quando necessário. Este conceito é designado 

por “convenções sobre configurações” (convention over configuration). Este conceito foi 

introduzido pelo Ruby on Rails. 

Page 5: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

Convenções: 

O nome de cada classe Controlador termina em Controller, e é colocada no diretório 

Controllers. 

Há um único diretório Views para todas as vistas da aplicação. 

As vistas que cada Controlador usa são colocadas num subdiretório de Views com o nome do 

Controlador (exceto o sufixo Controller). 

Todas as vistas partilhadas são colocadas no subdiretório de Views com o nome Shared. 

 

Especificação das Vistas 

O diretório Views contém um subdiretório por controlador, com o mesmo nome do 

controlador, mas sem o sufixo controller. Dentro de cada um destes subdiretórios existe um 

ficheiro view para cada método de ação, com o mesmo nome do método de ação. Este é o 

processo base de associação de vistas a métodos de ação. 

Exemplo1: 

Se o método de ação do controlador não especificar o nome da vista aplica‐se a convenção 

para localizar o objeto ViewResult retornado pelo método View(): é retornada uma vista com o 

mesmo nome do método de ação dentro do diretório Views/Nome_do_Controlador. 

Para o método /Livros/Index a vista selecionada será /Views/Livros/Index.cshtml  

            return View(); 

 

Exemplo2: 

Se pretendermos que o método de ação /Livros/Index retornasse uma vista diferente colocada 

no subdiretório Livros (por exemplo Teste.cshtml) temos de fornecer o nome da vista. 

            return View("Teste"); 

 

Exemplo3: 

Se pretendermos que o método de ação /Livros/Index retornasse uma vista diferente colocada 

num subdiretório diferente de Livros (por exemplo Teste.cshtml em /Views/Home/) temos de 

fornecer o nome completo  da vista usando sintaxe com ~. Nesta sintaxe com ~ também temos 

de fornecer a extensão porque não é usado o mecanismo interno de procura de vistas. 

  return View("~/Views/Home/Teste.cshtml"); 

 

 

Page 6: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

Vistas fortemente Tipadas 

Para passar um objeto modelo para a vista, podemos colocá‐lo como argumento do método 

View(). 

Na vista este objeto é colocado na propriedade Model. Mas esta propriedade é do tipo 

dinâmico, isto é, o seu tipo de dados só é determinado em tempo de execução. 

Podemos usá‐la deste modo mas também é possível atribuir‐lhe o tipo de dados correto. 

Atribuindo o tipo correto beneficiamos do Intellisense do Visual Studio quando acedemos às 

propriedades do objeto, e também às verificações em tempo de compilação (compile‐time 

checking). 

Para que lhe seja atribuído o tipo de dados correcto temos de indicar à vista o tipo de dados da 

propriedade Model com a seguinte declaração: 

@model MvcApplication1.Models.Livro 

 

View Models 

Muitas vezes uma vista tem necessidade de mostrar informação que não mapeia diretamente 

num objeto do modelo do domínio. 

Como na vista só podemos ter um objeto Model, uma possibilidade para mostrar informação 

extra que não faça parte do modelo da vista é enviá‐la através do ViewBag. Embora seja uma 

solução flexível, os dados obtidos pela vista através do VIewBag não são fortemente tipados, 

pelo que o programador da vista não terá a vantagem do intellisense do Visual Studio. 

Assim, pode ser útil construir uma classe para agregar toda a informação necessária para 

fornecer à vista, designada por ViewModel, um modelo (classe) construído com o único 

objectivo de fornecer informação para a vista. 

Uma classe ViewModel serve para encapsular múltiplos dados representados por instâncias de 

outras classes num objecto para passar à Vista. 

Não há nenhuma localização pré‐defenida para colocar os ViewModels. Normalmente cria‐se 

um diretório ViewModels na raiz do projeto MVC. Nesse diretório colocam‐se todos os 

ViewModels, um ficheiro para cada classe, com a designação do nome do Controlador seguida 

do nome do método de ação (ou Vista). 

 

Exemplo:  MvcApplication1   >   Add   >   New Folder:   ViewModels 

ViewModels/LivrosIndexViewModel.cs

    public class LivrosIndexViewModel {         public List<Livro> Livros { get; set; }         public List<Autor> Autores { get; set; }     } 

Page 7: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

Layouts 

Podemos usar um layout para definir um template comum para o site ou parte do site. Este 

template conterá um ou mais placeholders. As outras vistas da aplicação fornecerão 

conteúdos para estes placeholders.  

Exemplo: 

Layout1.cshtml

<!DOCTYPE html> <html>     <head><title>@ViewBag.Title</title></head>     <body>         <header> 

<h1>@ViewBag.Title</h1>         </header>         <div id="main‐content">@RenderBody()</div>         <footer>@RenderSection("Footer", required: false)</footer>     </body> </html>   

Index.cshtml  @{ 

Layout = "~/Views/Shared/Layout1.cshtml"; ViewBag.Title = "Index"; 

}  <p>Conteudo principal</p>  @section Footer { 

Este é o footer } 

@RenderBody() é um placeholder que marca o lugar onde as vistas que usam este layout 

colocarão o seu conteúdo principal. 

@RenderSection(“Footer”) é um placeholder que marca o lugar onde as vistas que usam este 

layout colocarão o conteúdo especificado na secção @section Footer. 

As vistas têm de fornecer conteúdo para todas as secções definidas no layout, a não ser que a 

secção seja definida como opcional: 

@RenderSection(“Footer”, required: false)  

Page 8: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

ViewStart 

Cada vista pode especificar a sua página de layout usando a propriedade Layout. 

Mas para um grupo de vistas que usem o mesmo layout podemos colocar num directório um 

ficheiro com o nome _ViewStart.cshtml. Este código é executado antes do código de qualquer 

vista nesse directório ou subdirectórios.  

_ViewStart.cshtml:

@{     Layout = "~/Views/Shared/_Layout.cshtml"; } 

3. Método Edit de LivrosController e correspondente vista Edit  

 Classe LivrosController, método Edit:   // GET: Livros/Edit/5 public ActionResult Edit(int? id) {   if (id == null) {     return new HttpStatusCodeResult(HttpStatusCode.BadRequest);   }   Livro livro = db.Livros.Find(id);   if (livro == null) {     return HttpNotFound();   }   return View(livro); }  // POST: Livros/Edit/5 // To protect from overposting attacks, please enable the specific properties you // want to bind to [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit([Bind(Include = "LivroId,Nome")] Livro livro) {   if (ModelState.IsValid)  {     db.Entry(livro).State = EntityState.Modified;     return RedirectToAction("Index");   }   return View(livro); }  

 O 2.º método Edit é precedido pelo atributo HttpPost, o que especifica que este método só é invocado por pedidos POST.  O 1.º método Edit poderia ter o atributo HttpGet, mas não é necessário, porque é assumido por omissão. Este método HttpGet só é invocado por pedidos GET.  Todos os métodos que criam, editam ou apagam dados devem ser invocados por POST. Modificar dados usando o método de envio GET cria riscos de segurança para além de violar as boas práticas http que especificam que os pedidos GET não devem mudar o estado da aplicação.  

Page 9: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

Processamento do pedido GET Este método recebe o id do Livro a editar. Este parâmetro é opcional (int? id), para que caso não o receba, evite erro de execução. Se o método Find() encontra o livro, retorna o objeto livro para o template vista. Se não encontra, o método HttpNotFound() é invocado.   Processamento do pedido POST O mecanismo Model Binder do framework ASP.NET recebe os valores do formulário enviados por POST, cria um objeto Livro, que é passado como parâmetro ao método de ação Edit. ModelState.IsValid verifica se todos os validadores tiveram sucesso.  O objeto Livro já existe na base de dados mas sofreu modificações. O método Entry() retorna o objecto DBEntityEntry desta entidade, fornecendo acesso a informação sobre a entidade e à capacidade de alterar essa informação. O estado do objeto é mudado para modificado. SaveChanges() guarda na base de dados todas as modificações feitas no contexto, executando o comando SQL Update para as entidades com a flag Modified. Todas as colunas da correspondente linha da tabela da base de dados são atualizadas, incluindo as que o utilizador não modificou. Em seguida o controlo é redirigido para o método de ação Index(), que mostra uma listagem dos livros já com este livro atualizado. Se a validação não teve sucesso o objeto livro que contém os valores recebidos do formulário é retornado para o template vista Edit.cshtml.   Template Edit.cshtml  @model WebApplication1.Models.Livro  @{   ViewBag.Title = "Edit"; }  <h2>Edit</h2>   @using (Html.BeginForm()) {   @Html.AntiForgeryToken()        <div class="form‐horizontal">     <h4>Livro</h4>     <hr />     @Html.ValidationSummary(true, "", new { @class = "text‐danger" })     @Html.HiddenFor(model => model.LivroId)      <div class="form‐group">       @Html.LabelFor(model => model.Titulo,              htmlAttributes: new { @class = "control‐label col‐md‐2" })       <div class="col‐md‐10">         @Html.EditorFor(model => model.Titulo,              new { htmlAttributes = new { @class = "form‐control" } })         @Html.ValidationMessageFor(model => model.Titulo, "",              new { @class = "text‐danger" })       </div>     </div>      <div class="form‐group">       <div class="col‐md‐offset‐2 col‐md‐10">         <input type="submit" value="Save" class="btn btn‐default" />       </div>     </div> 

Page 10: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

  </div> }  <div>   @Html.ActionLink("Back to List", "Index") </div>  @section Scripts {     @Scripts.Render("~/bundles/jqueryval") }     

4. Adicionar à aplicação a classe Editora  

 

1. Models  >  Adicionar classe 

Models  >  botão direito do rato:  Add  >   Class…   >        Name:  Editora.cs    Add 

 namespace MvcApplication1.Models {     public class Editora     {         public int EditoraId { get; set; }         public string Nome { get; set; }     } } 

 

 

Modificar a classe Livro 

    public class Livro     {         public int LivroId { get; set; }         public string Titulo { get; set; }         public virtual Editora Editora { get; set; } 

 } 

 

 

2. Incluir a entidade Editora no contexto de ligação à base de dados 

    public class BibliotecaDbContext : DbContext     {         public BibliotecaDbContext() : base("DefaultConnection") { }         public DbSet<Livro> Livros { get; set; }         public DbSet<Editora> Editoras { get; set; }     } 

 

3. Build 

 

Page 11: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

4. Scaffolding para criar Controladores e Vistas para a entidade Editora e 

novamente para a entidade Livro 

 

5.  Executar a aplicação (Start without Debugging = Ctrl+F5) 

Na barra de endereços do browser acrescentar /Livros    localhost:xxxx/Livros Erro na aplicação. 

 

6. Usar Migrações Code First  

Com Entity Framework Code First podemos escrever o modelo que a base de dados é gerada pelo Entity Framework. A funcionalidade Migrações permite migrar a base de dados ao longo do desenvolvimento, sempre que há alterações do modelo de dados. À medida que o código evolui, através do Package Manager podemos adicionar Migrações e atualizar a Base de Dados. A Configuração e os ficheiros de Migração são adicionados à solução do Visual Studio, pelo que todas as mudanças das migrações da base de dados ficam adicionadas ao código fonte.   

Quando corremos uma aplicação e obtemos erro informando: 

The model backing the 'MyClassContext' context has changed since the database was created. Consider 

using Code First Migrations to update the database ( http://go.microsoft.com/fwlink/?LinkId=238269). 

Tools –> Nuget Package Manager –> Package Manager Console 

Para ativar as migrações:  (enable‐migrations ‐contextTypeName MyClassContext) 

PM> enable‐migrations ‐contextTypeName BibliotecaDbContext 

Este comando adiciona ao projeto uma pasta Migrations contendo 1 ou 2 ficheiros: 

‐ Configuration.cs que permite configurar como as migrações se comportam para o nosso 

contexto. 

‐ Se Code First já criou a base de dados, também adiciona uma migração InitialCreate, com 

código que representa os objetos que já foram criados na base de dados. O nome do ficheiro 

inicia‐se por um timestamp para permitir ordenação. 

 

Para criar uma nova migração baseada nas alterações efetuadas ao modelo desde que a última 

migração foi criada:  (add‐migration nomeMigracao) 

PM> add‐migration AdicionadaClasseEditora 

Na pasta Migrations é criada uma nova migração com o nome iniciado por um timestamp. A 

classe definida neste ficheiro contém 2 métodos: 

‐ Up com as alterações a executar na base de dados para efetuar a migração. 

‐ Down com as alterações a executar na base de dados para desfazer a migração. 

Para efetuar a migração da base de dados podemos usar o código gerado automaticamente ou 

fazer modificações nesse código. 

 

Para aplicar a última migração criada à base de dados: 

PM> update‐database 

Page 12: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

7. Executar a aplicação (Start without Debugging = Ctrl+F5) 

 

 

  

      

8. Incluir a chave estrangeira na classe Livro 

    public class Livro     {         public int LivroId { get; set; }         public string Titulo { get; set; }         public int EditoraId { get; set; }         public virtual Editora Editora { get; set; } 

 } 

Page 13: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

9. Usar Migrações Code First  

PM> add‐migration ChaveEstrangeiraEditoraId 

PM> update‐database 

System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 

'EditoraId', table.Livroes'; column does not allow nulls. UPDATE fails.

Agora a propriedade EditoraId da classe Livro por ser do tipo int não pode ter valor nulo. Dá 

erro porque o objeto livro existente na base de dados tem a propriedade EditoraId nula. 

Migrar para uma versão específica (incluindo Downgrade) 

Para fazer o upgrade/downgrade da base de dados para o estado que estava após realizar uma migração 

específica, por exemplo migracao1:  

PM>  update‐database – targetMigration: migracao1 

Este commando executa os scripts UP ou Down para migrar para migracao1. 

Para desfazer até voltar a ter uma base de dados vazia executar o comando: 

PM>  update‐database –targetMigration: $InitialDatabase 

Executar: 

PM> update‐database –targetMigration $InitialDatabase 

PM> update‐database 

Todos os registos são apagados da base de dados. Update‐database efetua a migração da base 

de dados até à última migração criada. 

 

10. Scaffolding novamente para criar Controladores e Vistas para as entidades 

Editora e Livro 

 

11. Executar a aplicação (Start without Debugging = Ctrl+F5) 

Criar Editoras.   Criar Livros. 

 

Page 14: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

5. Método Create de LivrosController e correspondente vista Edit  

// GET: Livros/Create public ActionResult Create() {   ViewBag.EditoraId = new SelectList(db.Editoras, "EditoraId", "Nome");   return View(); }  // POST: Livros/Create // To protect from overposting attacks, please enable the specific properties you // want to bind to. [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(             [Bind(Include = "LivroId,Titulo,EditoraId")] Livro livro) {   if (ModelState.IsValid) {     db.Livros.Add(livro);     db.SaveChanges();     return RedirectToAction("Index");   }    ViewBag.EditoraId =            new SelectList(db.Editoras, "EditoraId", "Nome", livro.EditoraId);   return View(livro); }

O primeiro método Create é um método HttpGet, só executado em pedidos GET, usado para 

invocar a página Web com o formulário para ser preenchido pelo utilizador para criar objetos 

Livro. 

O segundo é um método HttpPost, só executado em pedidos POST, que recebe um objeto 

Livro criado com os dados introduzidos no formulário, e valida esses dados. Se a validação tem 

sucesso, guarda o objeto na base de dados, caso contrário, volta a apresentar o formulário 

com os dados que o utilizador preencheu. Caso a validação não tenha sucesso, o objeto Livro 

passado à vista contém os dados preenchidos pelo utilizador.  

Page 15: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

 

O objecto SelectList guarda os itens destinados a carregar um DropDownList. 

O objecto SelectList é colocado no ViewBag numa dada chave (EditoraId). 

ficheiro Create.cshtml 

@model WebApplication6.Models.Livro  @{   ViewBag.Title = "Create"; }  <h2>Create</h2>   @using (Html.BeginForm()) {   @Html.AntiForgeryToken()        <div class="form‐horizontal">     <h4>Livro</h4>     <hr />     @Html.ValidationSummary(true, "", new { @class = "text‐danger" })     <div class="form‐group">       @Html.LabelFor(model => model.Titulo,                htmlAttributes: new { @class = "control‐label col‐md‐2" })       <div class="col‐md‐10">         @Html.EditorFor(model => model.Titulo,                new { htmlAttributes = new { @class = "form‐control" } })         @Html.ValidationMessageFor(model => model.Titulo, "",                new { @class = "text‐danger" })       </div>     </div>      <div class="form‐group">       @Html.LabelFor(model => model.EditoraId, "EditoraId",                htmlAttributes: new { @class = "control‐label col‐md‐2" })       <div class="col‐md‐10">         @Html.DropDownList("EditoraId", null,                htmlAttributes: new { @class = "form‐control" })         @Html.ValidationMessageFor(model => model.EditoraId, "",                new { @class = "text‐danger" })       </div>     </div>      <div class="form‐group">       <div class="col‐md‐offset‐2 col‐md‐10">         <input type="submit" value="Create" class="btn btn‐default" />       </div>     </div>   </div> }  <div>   @Html.ActionLink("Back to List", "Index") </div>  @section Scripts {   @Scripts.Render("~/bundles/jqueryval") } 

Page 16: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

 

O método Html.DropDownList mostra os itens guardados no SelectList, usando a chave do 

ViewBag. 

Neste caso o SelectList guarda todas as Editoras existentes na base de dados, no objeto 

ViewBag, usando a chave EditoraId. 

O método Html.DropDownList carrega a DropDownList com o objeto existente no ViewBag na 

chave especificada EditoraId, ou seja, carrega com as Editoras existentes na base de dados. Se 

quiséssemos criar um livro com uma editora nova, teríamos primeiro de criar a editora e só 

depois poderíamos criar o livro.  

A variável model usada no ficheiro Create.cshtml representa o objeto fortemente tipado que é 

passado à vista pelo controlador com as restrições estabelecidas no modelo. A declaração 

@model efetua o casting para o tipo de dados do objeto. Através do objeto é possível 

conhecer as suas propriedades. 

6. Adicionar à aplicação a classe Autor  

 

1. Models  >  Adicionar classe 

Models  >  botão direito do rato:  Add  >   Class…   >        Name:  Autor.cs    Add 

 namespace MvcApplication1.Models {     public class Autor     {         public int AutorId { get; set; }         public string Nome { get; set; }         public virtual ICollection<Livro> Livros { get; set; }     } } 

 

 

Modificar a classe Livro 

    public class Livro     {         public int LivroId { get; set; }         public string Titulo { get; set; }         public int EditoraId { get; set; }         public virtual Editora Editora { get; set; }         public virtual ICollection<Autor> Autores { get; set; }  

 } 

 

 

Page 17: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

2. Incluir a entidade Autor no contexto de ligação à base de dados 

    public class BibliotecaDbContext : DbContext     {         public BibliotecaDbContext() : base("DefaultConnection") { }         public DbSet<Livro> Livros { get; set; }         public DbSet<Editora> Editoras { get; set; }         public DbSet<Autor> Autores { get; set; }     } 

 

3. Build 

 

4. Scaffolding para criar Controladores e Vistas para as entidades Autor e 

novamente para a entidade Livro 

 

5.  Executar a aplicação (Start without Debugging = Ctrl+F5) 

Na barra de endereços do browser acrescentar /Livros    localhost:xxxx/Livros Erro na aplicação. 

 

6. Usar Migrações Code First  

PM> add‐migration AdicionadaClasseAutor 

PM> update‐database

7. Executar a aplicação (Start without Debugging = Ctrl+F5) 

Criar Autores. 

Page 18: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File
Page 19: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

8. Criar interface para atribuir valores para a relação LivroAutores 

Vamos atribuir autores a livros na página Edit de Livro.

Para atribuir ou editar os autores de um livro vamos mostrar todos os autores existentes na base de dados, um checkbox para cada autor.

A vista não deve ter lógica de negócio, pelo que devemos passar à vista apenas a informação necessária para o funcionamento da interface gráfica. Vamos criar um modelo cujo único objetivo é passar informação para a vista, uma classe ViewModel designada AutorLivro. Como pretendemos atribuir ou editar os autores de um determinado livro, esta classe AutorLivro deve conter a seguinte informação: id do autor, nome do autor e se o autor já está atribuído ao livro.

Criar uma nova pasta ViewModels.

Nessa pasta criar uma classe com o nome AutorLivro

    public class AutorLivro     {         public int AutorId { get; set; }         public string Nome { get; set; }         public bool AutorDoLivro { get; set; }     }

Na classe LivrosController modificar o método HttpGet Edit:

Código inicial de Controllers/LivrosController.cs, método HttpGet Edit:

// GET: Livros/Edit/5 public ActionResult Edit(int? id) {   if (id == null) {     return new HttpStatusCodeResult(HttpStatusCode.BadRequest);   }   Livro livro = db.Livros.Find(id);   if (livro == null) { 

Page 20: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

    return HttpNotFound();   }   ViewBag.EditoraId = new SelectList(                 db.Editoras, "EditoraId", "Nome", livro.EditoraId);   return View(livro); } 

Código modificado de Controllers/LivrosController.cs, método HttpGet Edit:

// GET: Livros/Edit/5 public ActionResult Edit(int? id) {   if (id == null) {     return new HttpStatusCodeResult(HttpStatusCode.BadRequest);   }   // Find nao permite eager loading   //Livro livro = db.Livros.Find(id);   Livro livro = db.Livros               .Include(l => l.Autores)               .Where(l => l.LivroId == id)               .SingleOrDefault();   preencherAutoresDoLivro(livro);   if (livro == null) {     return HttpNotFound();   }   ViewBag.EditoraId = new SelectList(                   db.Editoras, "EditoraId", "Nome", livro.EditoraId);   return View(livro); } 

private void preencherAutoresDoLivro(Livro livro) {   var autores = db.Autores;   var autoresLivro = new HashSet<int>(livro.Autores.Select(a => a.AutorId));   var viewModel = new List<AutorLivro>();   foreach(var autor in autores) {     viewModel.Add(new AutorLivro {                 AutorId = autor.AutorId,                 Nome = autor.Nome,                 AutorDoLivro = autoresLivro.Contains(autor.AutorId)     });   }   ViewBag.Autores = viewModel; } 

Neste código o carregamento de um livro da base de dados inclui o carregamento da sua propriedade de navegação Autores (eager loading).

O método “preencherAutoresDoLivro” cria uma lista de objetos AutorLivro, um objeto AutorLivro para cada um dos autores existentes na base de dados. Cada objeto AutorLivro leva na propriedade boolean “AutorDoLivro” informação se atualmente é autor do livro. Esta lista de objetos AutorLivro é passada à vista em ViewBag.Autores.

Modificar Views/Livros/Edit.cshtml:

Código inicial de Views/Livros/Edit.cshtml:

@model MvcApplication1.Models.Livro 

Page 21: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

 @{   ViewBag.Title = "Edit"; }  <h2>Edit</h2>   @using (Html.BeginForm()) {   @Html.AntiForgeryToken()    <div class="form‐horizontal">     <h4>Livro</h4>     <hr />     @Html.ValidationSummary(true, "", new { @class = "text‐danger" })     @Html.HiddenFor(model => model.LivroId)      <div class="form‐group">       @Html.LabelFor(model => model.Titulo,                 htmlAttributes: new { @class = "control‐label col‐md‐2" })       <div class="col‐md‐10">         @Html.EditorFor(model => model.Titulo,                 new { htmlAttributes = new { @class = "form‐control" } })         @Html.ValidationMessageFor(model => model.Titulo, "",                 new { @class = "text‐danger" })       </div>     </div>      <div class="form‐group">       @Html.LabelFor(model => model.EditoraId, "EditoraId",                 htmlAttributes: new { @class = "control‐label col‐md‐2" })       <div class="col‐md‐10">         @Html.DropDownList("EditoraId", null,                     htmlAttributes: new { @class = "form‐control" })         @Html.ValidationMessageFor(model => model.EditoraId, "",                     new { @class = "text‐danger" })       </div>     </div>      <div class="form‐group">       <div class="col‐md‐offset‐2 col‐md‐10">         <input type="submit" value="Save" class="btn btn‐default" />       </div>     </div>    </div> }  <div>   @Html.ActionLink("Back to List", "Index") </div>  @section Scripts {   @Scripts.Render("~/bundles/jqueryval") } 

Código modificado de Views/Livros/Edit.cshtml:

@model MvcApplication1.Models.Livro  @{ 

Page 22: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

  ViewBag.Title = "Edit"; }  <h2>Edit</h2>   @using (Html.BeginForm()) {   @Html.AntiForgeryToken()    <div class="form‐horizontal">     <h4>Livro</h4>     <hr />     @Html.ValidationSummary(true, "", new { @class = "text‐danger" })     @Html.HiddenFor(model => model.LivroId)      <div class="form‐group">       @Html.LabelFor(model => model.Titulo,                 htmlAttributes: new { @class = "control‐label col‐md‐2" })       <div class="col‐md‐10">         @Html.EditorFor(model => model.Titulo,                 new { htmlAttributes = new { @class = "form‐control" } })         @Html.ValidationMessageFor(model => model.Titulo, "",                 new { @class = "text‐danger" })       </div>     </div>      <div class="form‐group">       @Html.LabelFor(model => model.EditoraId, "EditoraId",                 htmlAttributes: new { @class = "control‐label col‐md‐2" })       <div class="col‐md‐10">         @Html.DropDownList("EditoraId", null,                     htmlAttributes: new { @class = "form‐control" })         @Html.ValidationMessageFor(model => model.EditoraId, "",                     new { @class = "text‐danger" })       </div>     </div>      <div class="form‐group">       <div class="col‐md‐offset‐2 col‐md‐10">         <table>           <tr>             @{               int cnt = 0;               List< WebApplication6.ViewModels.AutorLivro> autores =                                   ViewBag.Autores;                foreach (var autor in autores) {                 if (cnt++ % 3 == 0) {                   @:</tr><tr>               }               @:<td>     <input type="checkbox" name="selectedAutores" value="@autor.AutorId"                @(Html.Raw(autor.AutorDoLivro ? "checked=\"checked\"" : "")) />                 @autor.AutorId @:  @autor.Nome                 @:</td>             }             @:</tr>           }         </table>       </div>     </div>      <div class="form‐group"> 

Page 23: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

      <div class="col‐md‐offset‐2 col‐md‐10">         <input type="submit" value="Save" class="btn btn‐default" />       </div>     </div>   </div> }  <div>   @Html.ActionLink("Back to List", "Index") </div>  @section Scripts {   @Scripts.Render("~/bundles/jqueryval") } 

Este código cria uma tabela html com 3 colunas. Cada célula tem um checkbox com o número e nome do autor. Todas as checkboxes têm o mesmo nome (name=”selectedAutores”) para serem tratadas com um grupo e para o browser enviar no parâmetro “selectedAutores” uma lista com os valores selecionados.

Na classe LivrosController modificar o método HttpPost Edit, executado quando o utilizador clica no botão Save.

Código inicial de Controllers/LivrosController.cs, método HttpPost Edit:

// POST: Livros/Edit/5 // To protect from overposting attacks, please enable the specific properties you want to bind to, for  // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit([Bind(Include = "LivroId,Titulo,EditoraId")] Livro livro) {   if (ModelState.IsValid) {     db.Entry(livro).State = EntityState.Modified;     db.SaveChanges();     return RedirectToAction("Index");   }   ViewBag.EditoraId = new SelectList(               db.Editoras, "EditoraId", "Nome", livro.EditoraId);   return View(livro); } 

Código modificado de Controllers/LivrosController.cs, método HttpPost Edit:

// POST: Livros/Edit/5 // To protect from overposting attacks, please enable the specific properties you want to bind to, for  // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit([Bind(Include = "LivroId,Titulo,EditoraId")] Livro livro,                             string[] selectedAutores) {   var logFile = new StreamWriter("C:/temp/log3.txt");   db.Database.Log = logFile.WriteLine;    if (ModelState.IsValid) {     Livro livroParaAtualizar = db.Livros.Find(livro.LivroId);     atualizarAutoresDoLivro(livroParaAtualizar, selectedAutores); 

Page 24: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

    db.Entry(livroParaAtualizar).State = EntityState.Modified;     db.SaveChanges();      logFile.Close();      return RedirectToAction("Index");   }   preencherAutoresDoLivro(livro);   ViewBag.EditoraId = new SelectList(                     db.Editoras, "EditoraId", "Nome", livro.EditoraId);    logFile.Close();    return View(livro); } 

 

private void atualizarAutoresDoLivro(Livro livroParaAtualizar,                             string[] selectedAutores) {   if (selectedAutores == null) {     livroParaAtualizar.Autores = new List<Autor>();     return;   }    var selectedAutoresHS = new HashSet<string>(selectedAutores);   var autoresLivro = new HashSet<int>(                     livroParaAtualizar.Autores.Select(a => a.AutorId));    var autores = db.Autores;   foreach (var autor in autores) {     // Se este autor foi um dos selecionados e     if (selectedAutoresHS.Contains(autor.AutorId.ToString())) {       // ainda não pertence à lista dos autores do livro       if (!autoresLivro.Contains(autor.AutorId)) {         // é adicionado à lista dos autores do livro         livroParaAtualizar.Autores.Add(autor);       }     }     else {       // Se este autor não foi um dos selecionados       // e pertence à lista dos autores do livro       if (autoresLivro.Contains(autor.AutorId)) {         // é removido da lista dos autores do livro         livroParaAtualizar.Autores.Remove(autor);       }     }    } } 

No código inicial o objeto livro era criado pelo Model Binder com os valores dos parâmetros recebidos no servidor e o seu estado marcado como Modified (o que faz automaticamente o attach ao contexto) para que SaveChanges o atualizasse (update) na base de dados (SaveChanges executa um Update porque o objeto livro tem id, campo escondido no browser).

Agora pretendemos atualizar as propriedades do livro incluindo os objetos constituintes da sua propriedade de navegação “Autores”. Esta propriedade Autores terá os mesmos

Page 25: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

objetos que já existiam na base de dados, eventualmente adicionando alguns e removendo outros.

O método “AtualizarAutoresDoLivro” atualiza a propriedade Autores do livro de acordo com o array de strings “selectedAutores” criado pelo Model Binder com os valores dos parâmetros recebidos no servidor ( o “name” de cada checkbox é “selectedAutores”).

Para que na página Index também seja possível ver os autores de cada livro vamos adicionar uma nova coluna com essa informação.

Código de Controllers/LivrosController.cs, método HttpGet Index:

// GET: Livros public ActionResult Index() {   var livros = db.Livros.Include(l => l.Editora);   return View(livros.ToList()); }

Código inicial de Views/Livros/Index.cshtml:

@model IEnumerable<MvcApplication1.Models.Livro>  @{   ViewBag.Title = "Index"; }  <h2>Index</h2>  <p>   @Html.ActionLink("Create New", "Create") </p> <table class="table">   <tr>     <th>       @Html.DisplayNameFor(model => model.Editora.Nome)     </th>     <th>       @Html.DisplayNameFor(model => model.Titulo)     </th>      <th></th>   </tr>  @foreach (var item in Model) {   <tr>     <td>       @Html.DisplayFor(modelItem => item.Editora.Nome)     </td>     <td>       @Html.DisplayFor(modelItem => item.Titulo)     </td>      <td>       @Html.ActionLink("Edit", "Edit", new { id=item.LivroId }) |       @Html.ActionLink("Details", "Details", new { id=item.LivroId }) | 

Page 26: Criação de uma aplicação Web ASP.NET MVC5 usando Code Firstmouta/ARQSI-2015-2016-1Sem/MVC5En… · Construção de uma aplicação ASP.NET MVC 5 usando Visual Studio 2015 File

      @Html.ActionLink("Delete", "Delete", new { id=item.LivroId })     </td>   </tr> }  </table>

Código modificado de Views/Livros/Index.cshtml:

@model IEnumerable<MvcApplication1.Models.Livro>  @{   ViewBag.Title = "Index"; }  <h2>Index</h2>  <p>   @Html.ActionLink("Create New", "Create") </p> <table class="table">   <tr>     <th>       @Html.DisplayNameFor(model => model.Editora.Nome)     </th>     <th>       @Html.DisplayNameFor(model => model.Titulo)     </th>     <th>Autores</th>      <th></th>   </tr>  @foreach (var item in Model) {   <tr>     <td>       @Html.DisplayFor(modelItem => item.Editora.Nome)     </td>     <td>       @Html.DisplayFor(modelItem => item.Titulo)     </td>      <td>       @{         foreach (var autor in item.Autores)         {           @autor.AutorId @:  @autor.Nome <br />         }       }     </td>       <td>       @Html.ActionLink("Edit", "Edit", new { id=item.LivroId }) |       @Html.ActionLink("Details", "Details", new { id=item.LivroId }) |       @Html.ActionLink("Delete", "Delete", new { id=item.LivroId })     </td>   </tr> }  </table>