01 在 ASP.NET Core 中开始使用 Razor Pages之创建电影模块CRUD

1 创建 Razor 页面 Web 应用

选择“ASP.NET Core Web 应用”模板

“项目名称”输入 RazorPagesMovie

选择“.NET 6.0 (长期支持)”

取消https

2 模板文件夹介绍

2.1 Pages 文件夹

包含 Razor 页面和支持文件。

每个 Razor 页面都是一对文件

支持文件的名称以下划线开头。 例如,_Layout.cshtml 文件可配置所有页面通用的 UI 元素。 此文件设置页面顶部的导航菜单和页面底部的版权声明。

2.2 wwwroot 文件夹

包含静态资产,如 HTML 文件、JavaScript 文件和 CSS 文件。

2.3 appsettings.json

包含配置数据,如连接字符串。

2.4 Program.cs

默认内容

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

3 添加数据模型

3.1 Movie模型

using System.ComponentModel.DataAnnotations;

namespace RazorPagesMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }
        public string Title { get; set; } = string.Empty;

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; } = string.Empty;
        public decimal Price { get; set; }
    }
}

3.2 通过基架工具生成页面

先要添加文件夹Movies

nuget包: Microsoft.EntityFrameworkCore.Design

右键单击 Pages/Movies 文件夹 >“添加”>“已搭建基架的新项”

使用实体框架的 Razor Pages (CRUD)

“模型类”下拉列表中,选择“Movie (RazorPagesMovie.Models)”

“添加数据上下文”对话框中,生成类名 RazorPagesMovie.Data.RazorPagesMovieContext

安装 Microsoft.EntityFrameworkCore.SqlServer

会生成如下文件:
Pages/Movies:“创建”、“删除”、“详细信息”、“编辑”和“索引”。
Data/RazorPagesMovieContext.cs

并会生成数据库连接配置

会注册RazorPagesMovieContext到容器

3.3 数据库迁移

# 第一次迁移
Add-Migration init
# 第一次更新数据库会创建数据库
Update-Database

3.4 初始化数据种子

SeedData.cs

using Microsoft.EntityFrameworkCore;
using RazorPageTest1.Models.MovieSystem;

namespace RazorPageTest1.Data
{
    public static class SeedData
    {
        public static void Initialize(IServiceProvider serviceProvider)
        {
            using (var context = new RazorPageTest1DbContext(
                serviceProvider.GetRequiredService<
                    DbContextOptions<RazorPageTest1DbContext>>()))
            {
                if (context == null || context.Movie == null)
                {
                    throw new ArgumentNullException("Null 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();
            }
        }
    }
}

项目启动时初始化种子:

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;

    SeedData.Initialize(services);
}

4 默认生成的页面

修改Movie模型

[Display(Name = “Release Date”)]

[Column(TypeName = “decimal(18, 2)”)]

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; } = string.Empty;

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; } = string.Empty;

        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }
    }
}

更新“编辑”、“详细信息”和“删除”Razor 页面以使用 {id:int} 路由模板。

@page 更改为 @page "{id:int}"

@page 更改为 @page "{id:int?}"

4.1 默认的并发异常处理

多个客户端的编辑按调用 SaveChanges 的顺序应用,后来应用的编辑可能会覆盖早期编辑的旧值。

如果编辑过程中,这条数据被删除了,会返回404

如果编辑过程中,这条数据被修改了,会保存最后修改的数据(所有字段)

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Attach(Movie).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!MovieExists(Movie.ID))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return RedirectToPage("./Index");
}

private bool MovieExists(int id)
{
  return (_context.Movie?.Any(e => e.ID == id)).GetValueOrDefault();
}

5 添加了按流派或名称搜索电影

Pages/Movies/Index.cshtml.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using RazorPageTest1.Models.MovieSystem;

namespace RazorPageTest1.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPageTest1.Data.RazorPageTest1DbContext _context;

        public IndexModel(RazorPageTest1.Data.RazorPageTest1DbContext context)
        {
            _context = context;
        }

        //筛选电影标题
        [BindProperty(SupportsGet =true)]
        public string? SearchString { get; set; }
        /// <summary>
        /// 类别列表
        /// </summary>
        public SelectList? Genres { get; set; }
        [BindProperty(SupportsGet = true)]
        //筛选电影类别
        public string? MovieGenre { get; set; }

        //电影列表数据
        public IList<Movie> Movie { get;set; } = default!;

        public async Task OnGetAsync()
        {
            IQueryable<string> genreQuery = from m in _context.Movie
                                            orderby m.Genre
                                            select m.Genre;

            Genres = new SelectList(await genreQuery.Distinct().ToListAsync());
            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);
            }
            Movie = await movies.ToListAsync();
        }
    }
}

增加筛选表单

<p>
    <a asp-page="Create">Create New</a>
</p>
<form>
    <p>
        <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" />
    </p>
</form>

6 向电影模型添加分级属性

public string Rating { get; set; } = string.Empty;

public class Movie
{
    public int ID { get; set; }
    public string Title { get; set; } = string.Empty;

    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string Genre { get; set; } = string.Empty;

    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }
    public string Rating { get; set; } = string.Empty;
}

6.1 修改视图

Pages/Movies/Index.cshtml

<th>
	@Html.DisplayNameFor(model => model.Movie[0].Rating)
</th>

<td>
	@Html.DisplayFor(modelItem => item.Rating)
</td>

Pages/Movies/Create.cshtml。
Pages/Movies/Delete.cshtml。
Pages/Movies/Details.cshtml。
Pages/Movies/Edit.cshtml
同样修改



6.2 使用 Code First 迁移更新数据库架构

add-migration movie_rating_add
update-database

修改数据种子

new Movie
{
    Title = "Ghostbusters ",
    ReleaseDate = DateTime.Parse("1984-3-13"),
    Genre = "Comedy",
    Price = 8.99M
    Rating="R"
},

7 将验证规则添加到电影模型

System.ComponentModel.DataAnnotations 命名空间提供以下内容:

  • 内置验证特性,可通过声明方式应用于类或属性。
  • [DataType] 等格式特性,这些特性可帮助进行格式设置,但不提供任何验证。
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }

        [StringLength(60, MinimumLength = 3)]
        [Required]
        public string Title { get; set; } = string.Empty;

        [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\s]*$")]
        [Required]
        [StringLength(30)]
        public string Genre { get; set; } = string.Empty;

        [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
        [StringLength(5)]
        [Required]
        public string Rating { get; set; } = string.Empty;
    }
}

7.1 客户端验证

@section Scripts {

@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}

}

Shared/_ValidationScriptsPartial.cshtml

<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

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

7.2 服务器端验证

浏览器中禁用 JavaScript

if (!ModelState.IsValid)
 {
    return Page();
 }

7.3 验证特性和格式特性

验证特性

[Required] 和 [MinimumLength] 特性指示属性必须具有一个值。 不阻止用户输入空格来满足此验证。

[RegularExpression] 特性用于限制可输入的字符。 在上述代码中,Genre:

只能使用字母。
第一个字母必须为大写。 允许使用空格,但不允许使用数字和特殊字符。
RegularExpressionRating:

要求第一个字符为大写字母。
允许在后续空格中使用特殊字符和数字。 “PG-13”对“分级”有效,但对于“Genre”无效。
[Range] 特性将值限制在指定的范围内。

[StringLength] 特性可以设置字符串属性的最大长度,以及可选的最小长度。

从本质上来说,需要值类型(如 decimal、int、float、DateTime),但不需要 [Required] 特性。

格式特性

[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; }

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

7.4 数据库迁移

Add-Migration add_validate
Update-Database
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值