【教程】ASP.Net Core-网站应用-Razor页面

原地址: 教程:使用 ASP.NET Core 创建 Razor Pages Web

  1. 新建 ASP.Net Core网站应用

将项目命名为“RazorPagesMovie”在下拉列表中选择“ ASP.NET Core 3.1”,然后依次选择“Web 应用程序”和“创建”;

2. 添加数据模型

using System;
using System.ComponentModel.DataAnnotations;
namespace RazorPagesMovie.Models
    public class Movie
        public int ID { get; set; }
        public string Title { get; set; }
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }

3. 添加基架,选择用实体框架的 Razor 页面 (CRUD)

4. 初始迁移

从“工具”菜单中,选择“NuGet 包管理器”>“包管理器控制台” 。输入以下指令:

Add-Migration InitialCreate
Update-Database

5. Razor指令说明

@ page Razor 指令将文件转换为一个 MVC 操作,这意味着它可以处理请求;

DisplayNameFor HTML 帮助程序检查 Lambda 表达式中引用的 Title 属性来确定显示名称。检查 Lambda 表达式(而非求值)。这意味着当 model model.Movie model.Movie[0] null 或为空时,不会存在任何访问冲突。对 Lambda 表达式求值时(例如,使用 @ Html.DisplayFor(modelItem => item.Title) ),将求得该模型的属性值。

@ model 指令指定传递到 Razor 页面的模型类型。

PageModel 基类包含 ViewData 字典属性,可用于将数据传递到某个视图。可以使用键/值模式将对象添加到 ViewData 字典。

在 Pages/_ViewStart.cshtml 文件中设置 Layout 属性。

@{
    Layout = "_Layout";
}

ASP.NET Core 配置 系统会读取 ConnectionString 。为了进行本地开发,它会从 appsettings.json 文件获取连接字符串。

6. 设置数据库种子

在 Models 文件夹中创建一个名为 SeedData 的新类:

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RazorPagesMovie.Data;
using System;
using System.Linq;
namespace RazorPagesMovie.Models
    public static class SeedData
        public static void Initialize(IServiceProvider serviceProvider)
            using (var context = new RazorPagesMovieContext(
                serviceProvider.GetRequiredService<
                    DbContextOptions<RazorPagesMovieContext>>()))
                // 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();

在 Program.cs 中,修改 Main 方法以执行以下操作:

  • 从依赖关系注入容器获取数据库上下文实例。
  • 调用 seed 方法,并将上下文传递给它。
  • Seed 方法完成时释放上下文。

下面的代码显示更新后的 Program.cs 文件。

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using RazorPagesMovie.Models;
using System;
namespace RazorPagesMovie
    public class Program
        public static void Main(string[] args)
            var host = CreateHostBuilder(args).Build();
            using (var scope = host.Services.CreateScope())
                var services = scope.ServiceProvider;
                    SeedData.Initialize(services);
                catch (Exception ex)
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred seeding the DB.");
            host.Run();
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                    webBuilder.UseStartup<Startup>();

7. 更新页面

修改Models/Movie.cs 文件:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
    public class Movie
        public int ID { get; set; }
        public string Title { get; set; }
        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }

[Column(TypeName = "decimal(18, 2)")] 数据注释使 Entity Framework Core 可以将 Price 正确映射到数据库中的货币。

Display 特性指定要显示的字段名称的内容(本例中应为“Release Date”,而不是“ReleaseDate”)。 DataType 属性指定数据的类型(日期),使字段中存储的时间信息不会显示。

标记帮助程序 使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素。在前面的代码中, AnchorTagHelper 从 Razor 页面(路由是相对的)、 asp-page 和路由 ID ( asp-route-id ) 动态生成 HTML href 特性值。

8. 添加搜索

将以下突出显示的属性添加到 Pages/Movies/Index.cshtml.cs:

[BindProperty(SupportsGet = true)]
public string SearchString { get; set; }
// Requires using Microsoft.AspNetCore.Mvc.Rendering;
public SelectList Genres { get; set; }
[BindProperty(SupportsGet = true)]
public string MovieGenre { get; set; }

[BindProperty] 会绑定名称与属性相同的表单值和查询字符串。在 GET 请求中进行绑定需要 (SupportsGet = true)

使用以下代码更新索引页面的 OnGetAsync 方法:

public async Task OnGetAsync()
    var movies = from m in _context.Movie
                 select m;
    if (!string.IsNullOrEmpty(SearchString))
        movies = movies.Where(s => s.Title.Contains(SearchString));
    Movie = await movies.ToListAsync();

导航到电影页面,并向 URL追加一个如 ?searchString=Ghost 的查询字符串,筛选的电影将显示出来。

如果向索引页面添加了以下路由模板,搜索字符串则可作为 URL 段传递:

@page "{searchString?}"

前面的路由约束允许按路由数据(URL 段)搜索标题,而不是按查询字符串值进行搜索。 "{searchString?}" 中的 ? 表示这是可选路由参数。

打开 Pages/Movies/Index.cshtml 文件,并添加以下代码中突出显示的 <form> 标记:

<form>
        Title: <input type="text" asp-for="SearchString" />
        <input type="submit" value="Filter" />
</form>

保存更改并测试筛选器。

使用以下代码更新 OnGetAsync 方法:

public async Task OnGetAsync()
    // Use LINQ to get list of genres.
    IQueryable<string> genreQuery = from m in _context.Movie
                                    orderby m.Genre
                                    select m.Genre;
    var movies = from m in _context.Movie
                 select m;
    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();

更新 Index.cshtml,如下所示:

<form>
        <select asp-for="MovieGenre" asp-items="Model.Genres">
            <option value="">All</option>
        </select>
        Title: <input type="text" asp-for="SearchString" />
        <input type="submit" value="Filter" />
</form>

通过按流派或/和电影标题搜索来测试应用。

9. 将新字段添加到 ASP.NET Core 中的 Razor 页面

使用 EF Code First 自动创建数据库时,Code First 将:

  • 向数据库添加 __EFMigrationsHistory 表格,以跟踪数据库的架构是否与从生成它的模型类同步。
  • 如果该模型类未与数据库同步,EF 将引发异常。

通过自动验证同步的架构/模型可以更容易地发现不一致的数据库/代码问题。

打开 Models/Movie.cs 文件,并添加 Rating 属性:

public string Rating { get; set; }

编辑 Pages/Movies/Index.cshtml,并添加 Rating 字段,更新“删除”、“详细信息”、“编辑”界面。

在 DB 更新为包括新字段之前,应用将不会正常工作。 在不更新数据库的情况下运行应用会引发 SqlException

SqlException: Invalid column name 'Rating'.

可通过几种方法解决此错误:

  1. 让 Entity Framework 自动丢弃并使用新的模型类架构重新创建数据库。 此方法在开发周期早期很方便;通过它可以一起快速改进模型和数据库架构。 此方法的缺点是会导致数据库中的现有数据丢失。 请勿对生产数据库使用此方法! 当架构更改时丢弃数据库并使用初始值设定项以使用测试数据自动设定数据库种子,这通常是开发应用的有效方式。
  2. 对现有数据库架构进行显式修改,使它与模型类相匹配。 此方法的优点是可以保留数据。 可以手动或通过创建数据库更改脚本进行此更改。
  3. 使用 Code First 迁移更新数据库架构。

对于本教程,请使用 Code First 迁移。

更新 SeedData 类,使它提供新列的值。示例更改如下所示,但可能需要对每个 new Movie 块做出此更改。

从“工具”菜单中,选择“NuGet 包管理器”>“包管理器控制台”。 在 PMC 中,输入以下命令:

Add-Migration Rating
Update-Database

Add-Migration 命令会通知框架执行以下操作:

  • Movie 模型与 Movie DB 架构进行比较。
  • 创建代码以将 DB 架构迁移到新模型。

名称“Rating”是任意的,用于对迁移文件进行命名。为迁移文件使用有意义的名称是有帮助的。

Update-Database 命令指示框架将架构更改应用到数据库并保留现有数据。

如果删除 DB 中的所有记录,种子初始值设定项会设定 DB 种子,并将包括 Rating 字段。可以使用浏览器中的删除链接,也可以从 Sql Server 对象资源管理器 (SSOX) 执行此操作。

另一个方案是删除数据库,并使用迁移来重新创建该数据库。 删除 SSOX 中的数据库:

  • 在 SSOX 中选择数据库。
  • 右键单击数据库,并选择“删除”。
  • 检查“关闭现有连接”。
  • 选择“确定”。
  • PMC 中更新数据库:
Update-Database

运行应用,并验证是否可以创建/编辑/显示具有 Rating 字段的电影。

10. 将验证添加到 ASP.NET Core Razor 页面

DataAnnotations 命名空间提供一组内置验证特性,可通过声明方式应用于类或属性。 DataAnnotations 还包含 DataType 等格式特性,有助于格式设置但不提供任何验证。

更新 Movie 类以使用内置的 Required StringLength RegularExpression Range 验证特性。

public class Movie
    public int ID { get; set; }
    [StringLength(60, MinimumLength = 3)]
    [Required]
    public string Title { get; set; }
    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    [Range(1, 100)]
    [DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }
    [RegularExpression(@"^[A-Z]+[a-zA-Z]*$")]
    [Required]
    [StringLength(30)]
    public string Genre { get; set; }
    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
    [StringLength(5)]