.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();
    }
}

运行结果:

相关推荐
铅笔侠_小龙虾1 小时前
Vue 学习目录
前端·vue.js·学习
LO嘉嘉VE1 小时前
学习笔记二十五:支持向量机-核函数
笔记·学习·支持向量机
Genevieve_xiao2 小时前
【数据结构与算法】【xjtuse】面向考纲学习(下)
java·数据结构·学习·算法
mpHH2 小时前
ivorysql 源码分析-双port兼容
数据库·学习·postgresql
北杳同学2 小时前
前端一些用得上的有意思网站
前端·javascript·vue.js·学习
小帅学编程2 小时前
JVM学习记录
jvm·学习
xian_wwq2 小时前
【学习笔记】威胁情报
网络·笔记·学习
小糊涂加油3 小时前
TypeScript学习笔记
笔记·学习
@游子3 小时前
Python学习笔记-Day6
笔记·python·学习