You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Entity Framework Core环境下jQuery DataTable无状态丢失更新方案咨询

我有一个基于.NET Core开发的应用,用到了Razor Pages和Entity Framework 5.0,用1.10.21版本的jQuery DataTable在局部页展示数据库数据。
我做了一个Add按钮,点击后会弹出Bootstrap 4.3.1版本的模态框让用户输入新对象的信息。提交成功后,新的Student对象会通过EF持久化到数据库,JavaScript会在Index页弹出通知,然后尝试更新DataTable加载最新数据。

目前我能用的DataTable刷新方式只有两种:

  • 调用window.location重载整个页面
  • 关闭模态框的jQuery代码请求承载DataTable的局部页的OnGet接口,用返回的更新后div替换原有div,因为DOM发生了变化,需要重新实例化DataTable

第二种方式基本能满足需求,不用重载整个页面,但因为要重新实例化DataTable,原来的筛选、排序状态都会被清空。
我知道DataTable有add()方法可以新增行,但所有示例都是用JSON作为参数,不支持直接传入EF对象。我后续还要实现行的删除、编辑功能,也会遇到同样的问题:更新DataTable的时候没法保留原有状态。

应@Rena要求,我附上了_StudentModalPartial.cshtml的代码,同时针对我尝试过的其他方案提出两个明确问题:

  • 通过isValid校验后,我可以不替换DataTable所在的div,而是返回构造好的<tr>元素添加到DataTable中。我知道这个方案可行,但没尝试实现,因为这个方案不够合理:虽然可以避免重新实例化DataTable、保留状态,但手动构造包含所有列、渲染后按钮和数据的<tr>非常繁琐,我实际的业务比这个学生示例复杂很多,维护难度很高,而且也解决不了删除和编辑场景的问题。
  • 目前我可以直接在HTML/DataTable中正常使用EF对象。如果调用row.add()、row.remove()等方法时没法用HTML作为数据源(看起来确实是这样),那么处理EF对象的最佳方案是什么?我看过一篇教程需要写大量逻辑把EF对象转换成JSON才能适配表格,应该有更简洁的实现方式。

Index.cshtml代码

@page
@model SuzRazorApp.Pages.Students.IndexModel

@{ 
    ViewData["Title"] = "Students";
}

<h2>Students</h2>

<div class="container-fluid pt-3 pb-3">
    <!-- Button trigger modal -->
    <button type="button" class="btn btn-primary" data-toggle="ajax-modal" data-url="@Url.Page("Index", "StudentModalPartial" )">
        Add <i class="fas fa-plus fa-sm"></i>
    </button>
</div>

<!-- Modal placeholder -->
<div id="modal-placeholder"></div>

<!-- Students table -->
<partial name="_StudentsTablePartial" model="Model.Students" />

@section Scripts
{
<script>
    jQuery(document).ready(function () {
        jQuery('#example').DataTable({
            stateSave: true,
            stateDuration: -1,
            columnDefs: [
                { orderable: false, targets: 3 },
                { searchable: false, targets: [2, 3] }
            ]
        });
    });

    jQuery(function () {
        jQuery('[data-toggle="tooltip"]').tooltip()
    })

</script>
}

Index.cshtml.cs代码

namespace SuzRazorApp.Pages.Students
{
    public class IndexModel : PageModel
    {
        private readonly SchoolContextContext _context;
        private readonly IConfiguration Configuration;

        public IndexModel(SchoolContextContext context, IConfiguration configuration)
        {
            _context = context;
            Configuration = configuration;
        }

        [BindProperty]
        public IList<Student> Students { get; set; }

        public async Task OnGetAsync()
        {
            Students = await _context.Students.AsNoTracking().ToListAsync();
        }

        public async Task<IActionResult> OnGetStudentsTablePartial()
        {
            Students = await _context.Students.AsNoTracking().ToListAsync();

            return Partial("_StudentsTablePartial", Students);
        }

        public PartialViewResult OnGetStudentModalPartial()
        {
            // 该接口返回携带空Student对象的_StudentModalPartial局部页
            return Partial("_StudentModalPartial", new Student { });
        }

        // 保存新增数据
        public async Task<IActionResult> OnPostStudentModalPartialAsync(Student model)
        {
            if (ModelState.IsValid)  // 页面有校验错误时不执行保存
            {
                _context.Students.Add(model);
                await _context.SaveChangesAsync();
            }

            // 重新返回模态框局部页,保留用户已填内容和校验错误(错误通过数据注解校验处理)
            return Partial("_StudentModalPartial", model);
        }
    }
}

DataTable对应局部页(_StudentTablePartial.cshtml)代码

@model IList<Models.Student>

<div id="divTable" class="container-fluid" data-url="@Url.Page("Index", "StudentsTablePartial")">
    <table id="example" class="table table-sm table-bordered table-striped" style="width:100%" >
        <thead>
            <tr>
                <th>
                    Last Name
                </th>
                <th>
                    First Name
                </th>
                <th>
                    Enrollment Date
                </th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Model)
            {
                <tr>
                    <td>
                        @Html.DisplayFor(modelItem => item.LastName)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.FirstMidName)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.EnrollmentDate)
                    </td>

                    <td style="white-space:nowrap">
                        <a asp-page="./Edit" asp-route-id="@item.Id"> <i class="fas fa-pencil-alt" data-toggle="tooltip" title="Edit"></i> </a>
                        <a asp-page="./Delete" asp-route-id="@item.Id"> <i class="far fa-trash-alt" data-toggle="tooltip" title="Delete"></i> </a>
                    </td>
                </tr>
            }
        </tbody>
    </table>
</div>

模态框保存后触发的site.js方法代码

// 按钮点击后在占位符加载模态框,为保存按钮绑定点击事件
$(function () {
    var placeholderElement = $('#modal-placeholder');

    $('button[data-toggle="ajax-modal"]').click(function (event) {
        var url = $(this).data('url');
        $.get(url).done(function (data) {
            placeholderElement.html(data);
            placeholderElement.find('.modal').modal('show');
        });
    });

    placeholderElement.on('click', '[data-save="modal"]', function (event) {
        // 阻止按钮默认点击行为
        event.preventDefault();

        // 读取表单数据并提交
        var form = $(this).parents('.modal').find('form');
        var actionUrl = form.attr('action');
        var dataToSend = form.serialize();

        // 从表单action属性读取接口地址发起POST请求
        $.post(actionUrl, dataToSend).done(function (data) {
            // data是渲染后的_ContactModalPartial局部页内容
            var newBody = $('.modal-body', data);
            // 替换当前模态框内容
            placeholderElement.find('.modal-body').replaceWith(newBody);

            // 校验通过则关闭模态框
            var isValid = newBody.find('[name="IsValid"]').val() === 'True';
            if (isValid) {
                $('#notificationsPlaceholder').find("p").text('数据保存成功!');
                $('#notificationsPlaceholder').show();

                var divTableElement = $('#divTable');
                var divTableUrl = divTableElement.data('url');
                $.get(divTableUrl).done(function (data) {  // data是接口返回的渲染后的表格局部页内容
                    //console.log(typeof data); // string
                    //console.log(data); // AJAX请求返回的HTML内容
                    divTableElement.html(data);   // 替换原有表格DOM
                    $('#example').DataTable();  // DOM更新后需要重新初始化DataTable插件
                });

                placeholderElement.find('.modal').modal('hide');
            }
        });
    });
});

_StudentModalPartial.cshtml代码

@model SuzRazorApp.Models.Student

<!-- Modal -->
<div class="modal fade" id="add-student" tabindex="-1" role="dialog" aria-labelledby="addStudentLabel" aria-hidden="true">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="addStudentLabel">Add Student</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">×</span>
                </button>
            </div>
            <div class="modal-body">
                <form asp-page-handler="StudentModalPartial">
                    <input name="IsValid" type="hidden" value="@ViewData.ModelState.IsValid.ToString()" />
                    <div class="form-group">
                        <label asp-for="FirstMidName"></label>
                        <input asp-for="FirstMidName" class="form-control" />
                        <span asp-validation-for="FirstMidName" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="LastName"></label>
                        <input asp-for="LastName" class="form-control" />
                        <span asp-validation-for="LastName" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="EnrollmentDate"></label>
                        <input asp-for="EnrollmentDate" class="form-control" />
                        <span asp-validation-for="EnrollmentDate" class="text-danger"></span>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                <button type="button" class="btn btn-primary" data-save="modal">Save</button>
            </div>
        </div>
    </div>
</div>

内容的提问来源于stack exchange,提问作者sldorman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 21:06:02