.NET Core 学习第二天:Razor Pages 新建页面及增删改查

上一节已经建好了实体类:Model/Student.cs,并建立了数据库。

_Layout.cshtml 中添加页面跳转路径:

1、StudentPage/Index.cshtml

典型的学生管理页面,实现了列表展示、搜索和删除功能。

右键-->选中添加-->选中Razor页面-->

前端代码:

html 复制代码
@page
@model RazorStudentManager.Pages.StudentPage.IndexModel
<br />
<div class="row">
    <h1 class="text-info col-4"> 学生列表 </h1>
</div>

<div class=" container p-0 m-0 ">
    <div class="row">
        <div class="col-4">
            <form method="get">
                <div class="input-group">
                    <input class="form-control" asp-for="Search" placeholder="请输入姓名查询" />
                    <div class="input-group-append">
                        <button type="submit" class="btn btn-primary">查询</button>
                    </div>
                </div>
            </form>
        </div>
        <div class="col-2 offset-6">
            <a asp-page="Create" class="btn btn-primary form-control text-white">创建学生</a>
        </div>
    </div>

    <form method="post">
        <table class="table m-2">
            <thead class="table-light">
                <tr>
                    <th>
                        <label scope="col" asp-for="Students.FirstOrDefault().Id"></label>
                    </th>
                    <th>
                        <label scope="col" asp-for="Students.FirstOrDefault().Name"></label>
                    </th>
            
                    <th>
                        <label scope="col" asp-for="Students.FirstOrDefault().Gender"></label>
                    </th>
                    <th>
                        <label scope="col" asp-for="Students.FirstOrDefault().Age"></label>
                    </th>
                    <th>
                        <label scope="col" asp-for="Students.FirstOrDefault().Phone"></label>
                    </th>
                    <th>
                        <label scope="col" asp-for="Students.FirstOrDefault().Address"></label>
                    </th>
                    <th>
                        <label scope="col" asp-for="Students.FirstOrDefault().Description"></label>
                    </th>

                    <th>
                        <label scope="col"> 操作</label>
                    </th>
                </tr>
            </thead>
            <tbody>
                @foreach (var item in Model.Students)
                {
                    <tr>
                        <th scope="row">@item.Id</th>
                        <td>@item.Name</td>
                        <td>@item.Gender</td>
                        <td>@item.Age</td>
                        <td>@item.Phone</td>
                        <td>@item.Address</td>
                        <td>@item.Description</td>
                        <td>
                            <button asp-page-handler="Delete" asp-route-id="@item.Id" class="btn btn-danger btn-sm" onclick="return confirm('您确定要删除吗??')">删除</button>
                            <a asp-route-id="@item.Id" asp-page="Edit" class="btn btn-success btn-sm">修改</a>
                        </td>
                    </tr>
                }
            </tbody>
        </table>
    </form>
</div>

后端代码:

cs 复制代码
/**
 * : PageModel: 继承自 PageModel 基类
 *      提供 Razor Pages 核心功能:ModelState、ViewData、Redirect 等
 *      包含 HTTP 请求处理方法:OnGet()、OnPost() 等
 */
public class IndexModel : PageModel
{

    /**
     * 私有字段声明 - 依赖注入:
     *      作用:声明数据库上下文依赖
     *      
     * private:访问修饰符,只能在当前类中访问     
     * readonly:只读字段,只能在构造函数中赋值
     * AppDbContext:Entity Framework Core 的数据库上下文类型
     * _db:字段名(C# 字段命名约定(下划线前缀 + 驼峰命名))
     * 
     * Java对应:private final AppDbContext _db;
     */
    private readonly AppDbContext _db;

    /**
     * 构造函数 - 依赖注入
     *      作用:通过构造函数注入依赖
     *      
     * 依赖注入模式: ASP.NET Core 自动创建 AppDbContext 实例并传入
     * 生命周期: 默认为 Scoped(每个HTTP请求一个实例)
     * 
     * Java/Spring对应:@Autowired private AppDbContext db;
     */
    public IndexModel(AppDbContext db)
    {
        _db = db; // 将注入的数据库上下文赋值给私有字段
    }

    /**
     * 公共属性 - 学生列表(用于视图显示)
     * 
     * 可在 Razor 视图中访问:@Model.Students
     * IEnumerable<Student>: 只读集合接口,支持迭代
     * 
     * 作用: 将数据从 PageModel 传递到 Razor 视图
     */
    public IEnumerable<Student> Students { get; set; }

    /**
     * 绑定属性 - 搜索关键字[前端 asp-for="Search" ]
     *                    
     * [BindProperty]: 模型绑定特性,自动将请求参数绑定到属性
     *          GET: ?search=关键字
     *          POST: 表单字段 <input name="Search">
     * SupportsGet = true: 允许从 GET 请求绑定(默认只支持POST)
     *                     通过设置 SupportsGet = true,可以扩展绑定功能,使属性也能接收 GET 请求中的查询字符串参数
     * 空值处理: 如果请求中没有 Search 参数,属性为 null
     */
    [BindProperty(SupportsGet = true)]  
    public string Search { get; set; }

    /**
     * HTTP GET 请求处理方法(查询操作)[命名约定: On + HTTP方法名]
     * 
     * async Task: 异步方法,不阻塞线程
     * 调用时机: 页面首次加载或 GET 请求时自动调用
     * 
     * 返回值: 通常为 void 或 Task(默认渲染同名页面)
     */
    public async Task OnGet()
    { 
        /**
         * 创建查询对象:
         * 
         * _db.Students: 访问 Students DbSet(数据库表)
         * AsNoTracking(): 重要优化方法!
         *      作用: 告诉 EF Core 不跟踪实体变更
         *      适用场景: 只读查询,不修改数据
         *      性能优势: 减少内存占用,提高查询速度
         *      生成的SQL: 没有变化,只是影响 EF Core 内部行为
         */
        var query = _db.Students.AsNoTracking();

        /*
         * 搜索条件处理
         * 
         * string.IsNullOrEmpty(): 检查字符串是否为 null 或空
         * query.Where(): LINQ 条件过滤
         * Lambda表达式: s => s.Name.Contains(Search)
         *              s: 每个 Student 对象
         *              s.Name.Contains(Search): 判断姓名是否包含搜索词
         *      SQL转换: 生成 WHERE Name LIKE '%{Search}%'
         */
        if (!string.IsNullOrEmpty(Search))
        {
            query = query.Where(s => s.Name.Contains(Search));
        }
        /*
         * 执行查询并赋值给属性
         * 
         * ToListAsync(): 异步执行查询并转换为列表
         * 延迟执行: 直到调用 ToListAsync() 才真正查询数据库
         * 异步等待: await 释放线程,等待数据库响应
         * 赋值: 将查询结果赋值给 Students 属性
         */
        Students = await query.ToListAsync();
    }

    /**
     * HTTP POST 请求处理方法(删除操作)[命名约定: OnPost + 操作名(Delete)]
     * 
     * 路由匹配: 表单提交时根据 asp-page-handler="Delete" 调用
     * 参数绑定: int id 自动从请求参数绑定
     * 
     * 返回值: IActionResult(重定向、视图、状态码等)
     */
    public async Task<IActionResult> OnPostDelete(int id)
    {
        /*
         * 根据ID查找学生实体
         * 
         * FindAsync(id): EF Core 的特殊查找方法
         *      先检查本地缓存,再查询数据库,只能根据主键查找,只能根据主键查找
         *      
         * 等价于: _db.Students.FirstOrDefaultAsync(s => s.Id == id)
         * SQL: SELECT TOP(1) * FROM Students WHERE Id = @id
         */
        var student = await _db.Students.FindAsync(id);

        /**
         * 找不到实体的处理
         * 
         * NotFound(): ControllerBase 方法,返回 404 状态码
         * 防止空引用异常: 避免对 null 对象进行操作
         * 用户体验: 告知用户资源不存在
         */
        if (student == null)
        {
            return NotFound();  // 返回404状态码
        }
        /**
         * 标记删除
         * 
         * Remove(): 将实体标记为删除状态
         * 重要: 此时还没删除数据库记录!
         * 内存操作: 只修改 EF Core 的变更跟踪器状态
         */
        _db.Students.Remove(student);

        /**
         * 将删除操作保存到数据库
         * 
         * SaveChangesAsync(): 提交所有挂起的更改到数据库
         * 批量操作: 可以同时处理多个添加、更新、删除
         * 事务性: 默认在事务中执行,失败则回滚
         */
        await _db.SaveChangesAsync();

        /**
         * 重定向回当前页面(刷新列表)
         * 
         * 作用: 执行 PRG 模式(Post-Redirect-Get)
         *       POST 处理完成后重定向到 GET,防止表单重复提交,刷新页面显示最新数据
         * */
        return RedirectToPage();
    }
}

运行结果:

2、StudentPage/Create.cshtml

前端代码:

html 复制代码
@page
@model RazorStudentManager.Pages.StudentPage.CreateModel
<br />
<h1 class="text-info"> Create </h1>
<br />
<div class="border container p-4">
    <form method="post">
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Name"></label>
            </div>
            <div class="col-6">
                <input class="form-control" asp-for="Student.Name" />
            </div>
            <span asp-validation-for="Student.Name" class="text-danger"></span>
        </div>
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Gender"></label>
            </div>
            <div class="col-6">
                <select class="form-select" asp-for="Student.Gender" asp-items="Html.GetEnumSelectList<Gender>()">
                    <option value="">请选择性别</option>
                </select>
            </div>
            <span asp-validation-for="Student.Gender" class="text-danger"></span>
        </div>
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Age"></label>
            </div>
            <div class="col-6">
                <input class="form-control" asp-for="Student.Age" />
            </div>
            <span asp-validation-for="Student.Age" class="text-danger"></span>
        </div>
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Phone"></label>
            </div>
            <div class="col-6">
                <input class="form-control" asp-for="Student.Phone" />
            </div>
            <span asp-validation-for="Student.Phone" class="text-danger"></span>
        </div>
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Address"></label>
            </div>
            <div class="col-6">
                <input class="form-control" asp-for="Student.Address" />
            </div>
        </div>
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Description"></label>
            </div>
            <div class="col-6">
                <input class="form-control" asp-for="Student.Description" />
            </div>
        </div>

        <div class="form-group row mb-3">
            <div class="col-3 offset-3">
                <input type="submit" value="创建" class="btn btn-primary form-control" />
            </div>
            <div class="col-3">
                <a asp-page="Index" class="btn btn-success form-control text-white">返回</a>
            </div>
        </div>
    </form>
</div>

后端代码:

cs 复制代码
public class CreateModel : PageModel
{
    private readonly AppDbContext _db;

    public CreateModel(AppDbContext db)
    {
        _db = db;//在构造里注入上下文
    }

    [BindProperty] //前后端绑定
    public Student Student { get; set; }

    public async Task<IActionResult> OnPost()
    {
        // ModelState.IsValid 是 ASP.NET MVC 中用于验证模型状态的属性,表示当前模型是否满足所有验证规则(如Required、StringLength等特性)。
        // 若验证通过返回 true,否则返回 false。
        if (ModelState.IsValid)  
        {
            await _db.AddAsync(Student); //将Student实体信息添加到数据库上下文
            await _db.SaveChangesAsync();//执行到数据库

            return RedirectToPage("Index");//重定向到Index页面
        }
        else
        {
            return Page();
        }
    }
}

运行结果:

3、StudentPage/Edit.cshtml

前端代码:

html 复制代码
@page
@model RazorStudentManager.Pages.StudentPage.EditModel
<br />
<h1 class="text-info"> Edit </h1>
<br />
<div class="border container p-4">
    <form method="post">
        <input type="hidden" asp-for="Student.Id" />
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Name"></label>
            </div>
            <div class="col-6">
                <input class="form-control" asp-for="Student.Name" />
            </div>
            <span asp-validation-for="Student.Name" class="text-danger"></span>
        </div>
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Gender"></label>
            </div>
            <div class="col-6">
                <select class="form-select" asp-for="Student.Gender" asp-items="Html.GetEnumSelectList<Gender>()">
                    <option value="">请选择性别</option>
                </select>
            </div>
            <span asp-validation-for="Student.Gender" class="text-danger"></span>
        </div>
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Age"></label>
            </div>
            <div class="col-6">
                <input class="form-control" asp-for="Student.Age" />
            </div>
            <span asp-validation-for="Student.Age" class="text-danger"></span>
        </div>
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Phone"></label>
            </div>
            <div class="col-6">
                <input class="form-control" asp-for="Student.Phone" />
            </div>
            <span asp-validation-for="Student.Phone" class="text-danger"></span>
        </div>
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Address"></label>
            </div>
            <div class="col-6">
                <input class="form-control" asp-for="Student.Address" />
            </div>
        </div>
        <div class="form-group row mb-3">
            <div class="col-3">
                <label asp-for="Student.Description"></label>
            </div>
            <div class="col-6">
                <input class="form-control" asp-for="Student.Description" />
            </div>
        </div>

        <div class="form-group row mb-3">
            <div class="col-3 offset-3">
                <input type="submit" value="修改" class="btn btn-primary form-control" />
            </div>
            <div class="col-3">
                <a asp-page="Index" class="btn btn-success form-control text-white">返回</a>
            </div>
        </div>
    </form>
</div>

后端代码:

cs 复制代码
public class EditModel : PageModel
{
    private readonly AppDbContext _db;

    public EditModel(AppDbContext db)
    {
        _db = db;
    }

    [BindProperty]
    public Student Student { get; set; }


    public async Task OnGet(int id)
    {
        //加载单个学生的信息
        Student = await _db.Students.FindAsync(id);

    }

    public async Task<IActionResult> OnPost()
    {
        if (ModelState.IsValid)
        {
            var studentObj = await _db.Students.FindAsync(Student.Id);

            studentObj.Name = Student.Name;
            studentObj.Gender = Student.Gender;
            studentObj.Age = Student.Age;
            studentObj.Phone = Student.Phone;
            studentObj.Address = Student.Address;
            studentObj.Description = Student.Description;

            await _db.SaveChangesAsync();
            return RedirectToPage("Index");
        }
        return RedirectToPage();
    }
}

运行结果:

相关推荐
西岸行者2 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意2 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码2 天前
嵌入式学习路线
学习
毛小茛2 天前
计算机系统概论——校验码
学习
babe小鑫2 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms2 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下2 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。2 天前
2026.2.25监控学习
学习
im_AMBER2 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J2 天前
从“Hello World“ 开始 C++
c语言·c++·学习