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

ASP.NET通过CSHTML页面删除数据库行时的异常问题:未点击删除按钮却清空整个数据库

问题根源分析

你遇到的核心问题是页面加载时就自动执行了所有行的删除操作,完全不需要点击按钮。罪魁祸首是你在Razor视图的按钮代码块里写了这段服务器端代码:

@{ var smth = new AdminController();
smth.delete_by_id(row.Id); //HERE IS WHERE I CALL THE QUERY
}

Razor里的@{ ... }属于服务器端执行代码,当用户访问页面时,服务器会先渲染整个视图:遍历数据库里的每一行数据,每遍历一行就实例化一次AdminController并调用delete_by_id(row.Id)——这就导致页面刚加载完成,所有用户数据已经被全部清空了。

另外,手动实例化Controller是不符合ASP.NET MVC设计模式的,框架应该负责Controller的创建和生命周期管理,手动实例化可能会带来上下文泄漏、依赖注入失效等问题。


正确实现方案

我们需要把删除操作改成用户点击按钮时才触发的异步请求,并且严格遵循MVC的分层原则。

1. 重构Controller的删除方法

首先把删除方法改成标准的Action,使用[HttpPost]标记(因为删除是修改操作,必须用POST而非GET,防止误触发),并且返回合适的JSON结果:

public class AdminController : BaseController
{
    private readonly UserContext _context;

    // 推荐用构造函数注入UserContext(如果你的项目支持依赖注入)
    public AdminController(UserContext context)
    {
        _context = context;
    }

    // 如果暂时无法使用依赖注入,保留原构造函数也可以,但尽量避免手动实例化
    // public AdminController()
    // {
    //     _context = new UserContext();
    // }

    [HttpPost] // 必须标记为POST,防止GET请求误触发删除
    public JsonResult DeleteById(int id)
    {
        try
        {
            // 先找到要删除的用户
            var user = _context.Users.FirstOrDefault(u => u.Id == id);
            if (user == null)
            {
                return Json(new { success = false, message = "用户不存在" });
            }

            // 执行删除操作(如果用EntityFramework Plus的DeleteFromQuery也可以,这里用原生EF示例)
            _context.Users.Remove(user);
            _context.SaveChanges();

            return Json(new { success = true });
        }
        catch (Exception ex)
        {
            // 实际项目中建议记录日志,这里简化处理
            return Json(new { success = false, message = $"删除失败: {ex.Message}" });
        }
    }

    // 重构Index方法,通过ViewModel传递用户数据(符合MVC模式)
    [AdminMod]
    public ActionResult Index()
    {
        SessionStatus();
        if ((string)System.Web.HttpContext.Current.Session["LoginStatus"] != "login")
        {
            return RedirectToAction("Index", "Login");
        }

        var currentUser = System.Web.HttpContext.Current.GetMySessionObject();
        var allUsers = _context.Users.ToList();

        // 创建ViewModel统一传递视图所需数据
        var viewModel = new AdminIndexViewModel
        {
            CurrentUser = new UserData
            {
                Username = currentUser.Username,
                Level = currentUser.Level
            },
            Users = allUsers
        };

        return View(viewModel);
    }
}

2. 创建ViewModel(统一传递视图数据)

新建一个ViewModel类,用来打包视图需要的所有数据:

public class AdminIndexViewModel
{
    public UserData CurrentUser { get; set; }
    public List<UDbTable> Users { get; set; }
}

3. 重构视图(用AJAX触发删除)

移除视图中直接执行的服务器端删除代码,改用AJAX在按钮点击时发送删除请求:

@model AdminIndexViewModel
@using WebApplication1.Domain.Enums
@{ 
    ViewBag.Title = "Admin";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<!-- 添加防CSRF攻击的Token -->
@Html.AntiForgeryToken()

<script>
$(document).ready(function() {
    // 给所有删除按钮绑定点击事件
    $('.delete-btn').click(function() {
        const userId = $(this).data('user-id');
        
        // 先弹出确认框,防止误操作
        if (!confirm("确定要删除这个用户吗?")) {
            return;
        }

        // 发送AJAX请求到删除接口
        $.ajax({
            url: '@Url.Action("DeleteById", "Admin")',
            type: 'POST',
            data: { id: userId },
            // 带上CSRF Token,防止跨站请求伪造
            headers: {
                RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val()
            },
            success: function(response) {
                if (response.success) {
                    // 删除成功后,移除页面上对应的行
                    $(event.target).closest('tr').remove();
                    alert("删除成功!");
                } else {
                    alert(`删除失败:${response.message}`);
                }
            },
            error: function() {
                alert("网络异常,删除失败!");
            }
        });
    });
});
</script>

<div class="container" style="margin-top: 1%;">
<h1>AdminPage</h1>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">#Id</th>
<th scope="col">Username</th>
<th scope="col">Email</th>
<th scope="col">Level</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@foreach (var user in Model.Users)
{
<tr>
<th scope="row">#@user.Id</th>
<td>@user.Username</td>
<td>@user.Email</td>
<td>
    @switch(user.Level)
    {
        case 0: <text>User</text>; break;
        case 1: <text>Premium</text>; break;
        default: <text>Admin</text>; break;
    }
</td>
<td>
    <!-- 删除按钮:用data属性存储用户ID,绑定统一的点击事件 -->
    <button type="button" class="btn btn-danger delete-btn" data-user-id="@user.Id" title="Delete User">
        <i class="fas fa-trash"></i>
    </button>
</td>
</tr>
}
</tbody>
</table>
</div>

关键注意点
  1. 服务器端代码vs客户端代码:Razor的服务器端代码会在页面渲染时执行,而客户端JS代码才是用户交互时触发的,一定要区分清楚两者的执行时机。
  2. HTTP方法规范:删除、修改这类写操作必须用POST(或PUT/DELETE),禁止用GET,防止浏览器预加载、爬虫等误触发操作。
  3. CSRF防护:ASP.NET MVC的@Html.AntiForgeryToken()必须加上,防止跨站请求伪造攻击。
  4. MVC模式遵循:尽量不要在视图中直接操作数据库,数据获取逻辑应该放在Controller或Service层,通过ViewModel传递给视图。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 19:57:51