Professional Documents
Culture Documents
O que é um Layout
A maioria dos aplicativos web têm um layout comum que fornece aos usuários
uma experiência consistente conforme eles navegam de uma página. O layout
normalmente inclui elementos de interface do usuário comum, como o
cabeçalho de aplicativo, navegação ou elementos de menu e rodapé.
Estruturas HTML comuns, como scripts e folhas de estilo são também usadas
pelo número de páginas dentro de um aplicativo. Todos esses elementos
compartilhados podem ser definidos em uma layout arquivo, que pode ser
referenciado por qualquer exibição usada dentro do aplicativo. Layouts de
reduzem código duplicado em exibições, ajudando a seguir o princípio não
repita por conta própria (teste).
Um exemplo _Layout.cshtml :
htmlCopiar
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - WebApplication1</title>
<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css"
/>
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet"
href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-
property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-
version="true" />
</environment>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-
toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a asp-area="" asp-controller="Home" asp-action="Index"
class="navbar-brand">WebApplication1</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a asp-area="" asp-controller="Home" asp-
action="Index">Home</a></li>
<li><a asp-area="" asp-controller="Home" asp-
action="About">About</a></li>
<li><a asp-area="" asp-controller="Home" asp-
action="Contact">Contact</a></li>
</ul>
@await Html.PartialAsync("_LoginPartial")
</div>
</div>
</div>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>© 2016 - WebApplication1</p>
</footer>
</div>
<environment names="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-
2.2.0.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery">
</script>
<script
src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
asp-fallback-test="window.jQuery && window.jQuery.fn &&
window.jQuery.fn.modal">
</script>
<script src="~/js/site.min.js" asp-append-version="true"></script>
</environment>
@RenderSection("scripts", required: false)
</body>
</html>
Especificando um Layout
Modos de exibição Razor têm um Layout propriedade. Modos de exibição
individuais Especifica um layout definindo essa propriedade:
htmlCopiar
@{
Layout = "_Layout";
}
Por padrão, todo layout deve chamar RenderBody . Sempre que a chamada
para RenderBody é colocada, o conteúdo do modo de exibição será renderizado.
<a name=layout-sections-label>
Seções
@section Scripts {
<script type="text/javascript" src="/scripts/main.js"></script>
}
Ignorando seções
<a name=viewimports>
@addTagHelper
@removeTagHelper
@tagHelperPrefix
@using
@model
@inherits
@inject
@using WebApplication1
@using WebApplication1.Models
@using WebApplication1.Models.AccountViewModels
@using WebApplication1.Models.ManageViewModels
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<a name=viewstart>
@{
Layout = "_Layout";
}
As classes de modelo que você cria são conhecidas como classes de dados
POCO (de "objetos CLR básicos") porque elas não têm nenhuma dependência
do EF Core. Elas definem as propriedades dos dados que são armazenados no
banco de dados.
using System;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
}
using Microsoft.EntityFrameworkCore;
namespace RazorPagesMovie.Models
{
public class MovieContext : DbContext
{
public MovieContext(DbContextOptions<MovieContext> options)
: base(options)
{
}
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": {
"MovieContext": "Server=(localdb)\\mssqllocaldb;Database=Movie-
1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
services.AddDbContext<MovieContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MovieContext")));
services.AddMvc();
}
consoleCopiar
-m O nome do modelo.
Testar o aplicativo
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
using RazorPagesMovie.Models;
namespace RazorPagesMovie.Pages.Movies
{
public class IndexModel : PageModel
{
private readonly RazorPagesMovie.Models.MovieContext _context;
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Title)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Movie) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
A diretiva @model
cshtmlCopiar
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
ViewData e layout
Considere o código a seguir:
cshtmlCopiar
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
@{
ViewData["Title"] = "Index";
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - RazorPagesMovie</title>
@{
Layout = "_Layout";
}
Atualizar o layout
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Movie</title>
namespace RazorPagesMovie.Pages.Movies
{
public class CreateModel : PageModel
{
private readonly RazorPagesMovie.Models.MovieContext _context;
[BindProperty]
public Movie Movie { get; set; }
_context.Movie.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
_context.Movie.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
@page
@model RazorPagesMovie.Pages.Movies.CreateModel
@{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-
danger"></div>
<div class="form-group">
<label asp-for="Movie.Title" class="control-label"></label>
<input asp-for="Movie.Title" class="form-control" />
<span asp-validation-for="Movie.Title" class="text-
danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-
label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-
danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.Genre" class="control-label"></label>
<input asp-for="Movie.Genre" class="form-control" />
<span asp-validation-for="Movie.Genre" class="text-
danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.Price" class="control-label"></label>
<input asp-for="Movie.Price" class="form-control" />
<span asp-validation-for="Movie.Price" class="text-
danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default"
/>
</div>
</form>
</div>
</div>
<div>
<a asp-page="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
O Visual Studio exibe a marca <form method="post"> em uma fonte diferente
usada para os auxiliares de marcas. O elemento <form method="post"> é
um auxiliar de marcas de formulário. O auxiliar de marcas de formulário inclui
automaticamente um token antifalsificação.
services.AddDbContext<MovieContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MovieContext")));
services.AddMvc();
}
"ConnectionStrings": {
"MovieContext": "Server=(localdb)\\mssqllocaldb;Database=Movie-
1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
namespace RazorPagesMovie.Models
{
public static class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = new MovieContext(
serviceProvider.GetRequiredService<DbContextOptions<MovieContext>>()))
{
// Look for any movies.
if (context.Movie.Any())
{
return; // DB has been seeded
}
context.Movie.AddRange(
new Movie
{
Title = "When Harry Met Sally",
ReleaseDate = DateTime.Parse("1989-2-12"),
Genre = "Romantic Comedy",
Price = 7.99M
},
new Movie
{
Title = "Ghostbusters",
ReleaseDate = DateTime.Parse("1984-3-13"),
Genre = "Comedy",
Price = 8.99M
},
new Movie
{
Title = "Ghostbusters 2",
ReleaseDate = DateTime.Parse("1986-2-23"),
Genre = "Comedy",
Price = 9.99M
},
new Movie
{
Title = "Rio Bravo",
ReleaseDate = DateTime.Parse("1959-4-15"),
Genre = "Western",
Price = 3.99M
}
);
context.SaveChanges();
}
}
}
}
if (context.Movie.Any())
{
return; // DB has been seeded.
}
namespace RazorPagesMovie
{
public class Program
{
public static void Main(string[] args)
{
var host = BuildWebHost(args);
try
{
// Requires using RazorPagesMovie.Models;
SeedData.Initialize(services);
}
catch (Exception ex)
{
var logger =
services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
host.Run();
}
Testar o aplicativo
using System;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
Clique com o botão direito do mouse em uma linha curvada vermelha > **
Ações Rápidas e Refatorações.
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Atualize as Páginas Editar, Detalhes e Excluir do Razor para que elas usem o
modelo de rota “{id:int}”. Altere a diretiva de página de cada uma dessas
páginas para @page "{id:int}" . Execute o aplicativo e, em seguida, exiba o
código-fonte. O HTML gerado adiciona a ID à parte do caminho da URL:
htmlCopiar
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Uma solicitação para a página com o modelo de rota “{id:int}” que não inclui o
inteiro retornará um erro HTTP 404 (não encontrado). Por
exemplo, http://localhost:5000/Movies/Details retornará um erro 404. Para
tornar a ID opcional, acrescente ? à restrição de rota:
cshtmlCopiar
@page "{id:int?}"
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!_context.Movie.Any(e => e.ID == Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
[BindProperty]
public Movie Movie { get; set; }
if (Movie == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!_context.Movie.Any(e => e.ID == Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
}
Quando uma solicitação HTTP GET é feita para a página Movies/Edit (por
exemplo, http://localhost:5000/Movies/Edit/2 ):
[BindProperty]
public Movie Movie { get; set; }
Os métodos HTTP GET nas páginas Índice, Criar e Excluir do Razor seguem um
padrão semelhante. O método OnPostAsync HTTP POST na página Criar do
Razor segue um padrão semelhante ao método OnPostAsync na página Editar
do Razor.
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
A primeira linha do método OnGetAsync cria uma consulta LINQ para selecionar
os filmes:
C#Copiar
A consulta é somente definida neste ponto; ela não foi executada no banco de
dados.
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
@page "{searchString?}"
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<form>
<p>
Title: <input type="text" name="SearchString">
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
@*Markup removed for brevity.*@
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
if (!String.IsNullOrEmpty(movieGenre))
{
movies = movies.Where(x => x.Genre == movieGenre);
}
Genres = new SelectList(await genreQuery.Distinct().ToListAsync());
Movie = await movies.ToListAsync();
}
O código a seguir é uma consulta LINQ que recupera todos os gêneros do
banco de dados.
C#Copiar
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<form>
<p>
<select asp-for="MovieGenre" asp-items="Model.Genres">
<option value="">All</option>
</select>
<table class="table">
<thead>
Nesta seção, você usará as Migrações do Entity Framework Code First para
adicionar um novo campo ao modelo e migrar essa alteração ao banco de
dados.
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<form>
<p>
<select asp-for="MovieGenre" asp-items="Model.Genres">
<option value="">All</option>
</select>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Title)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Price)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Rating)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Movie) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.Rating)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
@page
@model RazorPagesMovie.Pages.Movies.CreateModel
@{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-
danger"></div>
<div class="form-group">
<label asp-for="Movie.Title" class="control-label"></label>
<input asp-for="Movie.Title" class="form-control" />
<span asp-validation-for="Movie.Title" class="text-
danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-
label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-
danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.Genre" class="control-label"></label>
<input asp-for="Movie.Genre" class="form-control" />
<span asp-validation-for="Movie.Genre" class="text-
danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.Price" class="control-label"></label>
<input asp-for="Movie.Price" class="form-control" />
<span asp-validation-for="Movie.Price" class="text-
danger"></span>
</div>
<div class="form-group">
<label asp-for="Movie.Rating" class="control-label"></label>
<input asp-for="Movie.Rating" class="form-control" />
<span asp-validation-for="Movie.Rating" class="text-
danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default"
/>
</div>
</form>
</div>
</div>
<div>
<a asp-page="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
O aplicativo não funcionará até que o BD seja atualizado para incluir o novo
campo. Se for executado agora, o aplicativo gerará uma SqlException :
Copiar
Esse erro é causado devido à classe de modelo Movie atualizada ser diferente
do esquema da tabela Movie do banco de dados. (Não há nenhuma
coluna Rating na tabela de banco de dados.)
context.Movie.AddRange(
new Movie
{
Title = "When Harry Met Sally",
ReleaseDate = DateTime.Parse("1989-2-12"),
Genre = "Romantic Comedy",
Price = 7.99M,
Rating = "R"
},
Compile a solução.
Add-Migration Rating
Update-Database
PowerShellCopiar
Update-Database
Validação
Um princípio-chave do desenvolvimento de software é
chamado DRY (“Don't Repeat Yourself”).As Páginas Razor incentivam o
desenvolvimento quando a funcionalidade é especificada uma vez e ela é
refletida em todo o aplicativo. O DRY pode ajudar a reduzir a quantidade de
código em um aplicativo. O DRY faz com que o código seja menos propenso a
erros e mais fácil de testar e manter.
[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
}
Os dados de formulário não são postados no servidor até que não haja erros de
validação do lado do cliente. Verifique se os dados de formulário não são
postados por uma ou mais das seguintes abordagens:
Validação do servidor
if (!ModelState.IsValid)
{
return Page();
}
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Movie.Title" class="control-label"></label>
<input asp-for="Movie.Title" class="form-control" />
<span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>
[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
O atributo DisplayFormat pode ser usado por si só, mas geralmente é uma boa
ideia usar o atributo DataType . O atributo DataType transmite a semântica dos
dados, ao invés de apresentar como renderizá-lo em uma tela e oferece os
seguintes benefícios que você não obtém com DisplayFormat:
Geralmente, não é uma boa prática compilar datas rígidas nos modelos e,
portanto, o uso do atributo Range e de DateTime não é recomendado.
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$"), StringLength(5)]
public string Rating { get; set; }
}
Publicar no Azure
Recursos adicionais
Trabalhando com formulários
Globalização e localização
Introdução aos auxiliares de marcação
Criando auxiliares de marcação
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using RazorPagesMovie.Models;
namespace RazorPagesMovie.Utilities
{
public class FileHelpers
{
public static async Task<string> ProcessFormFile(IFormFile formFile,
ModelStateDictionary modelState)
{
var fieldDisplayName = string.Empty;
if (property != null)
{
var displayAttribute =
property.GetCustomAttribute(typeof(DisplayAttribute)) as DisplayAttribute;
if (displayAttribute != null)
{
fieldDisplayName = $"{displayAttribute.Name} ";
}
}
// Use Path.GetFileName to obtain the file name, which will
// strip any path information passed as part of the
// FileName property. HtmlEncode the result in case it must
// be returned in an error message.
var fileName =
WebUtility.HtmlEncode(Path.GetFileName(formFile.FileName));
if (formFile.ContentType.ToLower() != "text/plain")
{
modelState.AddModelError(formFile.Name, $"The
{fieldDisplayName}file ({fileName}) must be a text file.");
}
return string.Empty;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Schedule
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name="Uploaded (UTC)")]
[DisplayFormat(DataFormatString = "{0:F}")]
public DateTime UploadDT { get; set;}
}
}
Atualizar o MovieContext
Especifique um DbSet no MovieContext (Models/MovieContext.cs) para os
agendamentos:
C#Copiar
using Microsoft.EntityFrameworkCore;
namespace RazorPagesMovie.Models
{
public class MovieContext : DbContext
{
public MovieContext(DbContextOptions<MovieContext> options)
: base(options)
{
}
Add-Migration AddScheduleTable
Update-Database
using Microsoft.AspNetCore.Http;
using System.ComponentModel.DataAnnotations;
namespace RazorPagesMovie.Models
{
public class FileUpload
{
[Required]
[Display(Name="Title")]
[StringLength(60, MinimumLength = 3)]
public string Title { get; set; }
[Required]
[Display(Name="Public Schedule")]
public IFormFile UploadPublicSchedule { get; set; }
[Required]
[Display(Name="Private Schedule")]
public IFormFile UploadPrivateSchedule { get; set; }
}
}
@page
@model RazorPagesMovie.Pages.Schedules.IndexModel
@{
ViewData["Title"] = "Schedules";
}
<h2>Schedules</h2>
<hr />
<h3>Upload Schedules</h3>
<div class="row">
<div class="col-md-4">
<form method="post" enctype="multipart/form-data">
<div class="form-group">
<label asp-for="FileUpload.Title" class="control-
label"></label>
<input asp-for="FileUpload.Title" type="text" class="form-
control" />
<span asp-validation-for="FileUpload.Title" class="text-
danger"></span>
</div>
<div class="form-group">
<label asp-for="FileUpload.UploadPublicSchedule"
class="control-label"></label>
<input asp-for="FileUpload.UploadPublicSchedule" type="file"
class="form-control" style="height:auto" />
<span asp-validation-for="FileUpload.UploadPublicSchedule"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FileUpload.UploadPrivateSchedule"
class="control-label"></label>
<input asp-for="FileUpload.UploadPrivateSchedule" type="file"
class="form-control" style="height:auto" />
<span asp-validation-for="FileUpload.UploadPrivateSchedule"
class="text-danger"></span>
</div>
<input type="submit" value="Upload" class="btn btn-default" />
</form>
</div>
</div>
<h3>Loaded Schedules</h3>
<table class="table">
<thead>
<tr>
<th></th>
<th>
@Html.DisplayNameFor(model => model.Schedule[0].Title)
</th>
<th>
@Html.DisplayNameFor(model => model.Schedule[0].UploadDT)
</th>
<th class="text-center">
@Html.DisplayNameFor(model =>
model.Schedule[0].PublicScheduleSize)
</th>
<th class="text-center">
@Html.DisplayNameFor(model =>
model.Schedule[0].PrivateScheduleSize)
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Schedule) {
<tr>
<td>
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.UploadDT)
</td>
<td class="text-center">
@Html.DisplayFor(modelItem => item.PublicScheduleSize)
</td>
<td class="text-center">
@Html.DisplayFor(modelItem => item.PrivateScheduleSize)
</td>
</tr>
}
</tbody>
</table>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;
using RazorPagesMovie.Utilities;
namespace RazorPagesMovie.Pages.Schedules
{
public class IndexModel : PageModel
{
private readonly RazorPagesMovie.Models.MovieContext _context;
[BindProperty]
public FileUpload FileUpload { get; set; }
var publicScheduleData =
await
FileHelpers.ProcessFormFile(FileUpload.UploadPublicSchedule, ModelState);
var privateScheduleData =
await
FileHelpers.ProcessFormFile(FileUpload.UploadPrivateSchedule, ModelState);
_context.Schedule.Add(schedule);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
[BindProperty]
public FileUpload FileUpload { get; set; }
O modelo também usa uma lista dos agendamentos ( IList<Schedule> ) para
exibir os agendamentos armazenados no banco de dados na página:
C#Copiar
var publicScheduleData =
await FileHelpers.ProcessSchedule(FileUpload.UploadPublicSchedule,
ModelState);
var privateScheduleData =
await FileHelpers.ProcessSchedule(FileUpload.UploadPrivateSchedule,
ModelState);
// Perform a second check to catch ProcessSchedule method
// violations.
if (!ModelState.IsValid)
{
Schedule = await _context.Schedule.AsNoTracking().ToListAsync();
return Page();
}
_context.Schedule.Add(schedule);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
@page "{id:int}"
@model RazorPagesMovie.Pages.Schedules.DeleteModel
@{
ViewData["Title"] = "Delete Schedule";
}
<h2>Delete Schedule</h2>
<form method="post">
<input type="hidden" asp-for="Schedule.ID" />
<input type="submit" value="Delete" class="btn btn-default" /> |
<a asp-page="./Index">Back to List</a>
</form>
</div>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;
namespace RazorPagesMovie.Pages.Schedules
{
public class DeleteModel : PageModel
{
private readonly RazorPagesMovie.Models.MovieContext _context;
[BindProperty]
public Schedule Schedule { get; set; }
if (Schedule == null)
{
return NotFound();
}
return Page();
}
if (Schedule != null)
{
_context.Schedule.Remove(Schedule);
await _context.SaveChangesAsync();
}
return RedirectToPage("./Index");
}
}
}
if (Schedule != null)
{
_context.Schedule.Remove(Schedule);
await _context.SaveChangesAsync();
}
return RedirectToPage("./Index");
}
Após a exclusão com êxito do agendamento, o RedirectToPage envia o usuário
para a página Index.cshtml dos agendamentos.
Solução de problemas
Para solucionar problemas de informações com o carregamento de IFormFile ,
consulte Carregamentos de arquivos no ASP.NET Core: solução de problemas.
Recursos adicionais
Carregamentos de arquivos no ASP.NET Core
IFormFile