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

ASP.NET Core+EF中如何防止增改功能出现重复条目?

解决表单创建/编辑时重复字段的问题

要实现FirstName、LastName和DOB组合唯一的约束,并在重复时显示错误提示,需要从数据库约束和业务逻辑检查两方面入手,以下是具体方案:

1. 确保数据库唯一索引生效

之前的两种索引配置方式(特性或Fluent API)本身是正确的,但需要执行EF Core迁移才能将索引同步到数据库:

使用Fluent API配置索引(推荐)

在你的DbContext中保留这段代码:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<MRegForm>()
        .HasIndex(x => new { x.FirstName, x.LastName, x.DOB })
        .IsUnique();
}

public DbSet<MRegForm> MRegForms { get; set; } = default!;

执行迁移命令

打开包管理器控制台,运行以下命令更新数据库:

Add-Migration AddUniqueIndexForNameAndDOB
Update-Database

这会在数据库中创建组合唯一索引,从底层阻止重复数据插入。

2. 在业务逻辑中添加重复检查与错误提示

仅靠数据库约束会抛出异常,需要在OnPostAsync中提前检查或捕获异常,将错误信息添加到模型状态,返回页面显示提示。

修改创建方法的OnPostAsync

public async Task<IActionResult> OnPostAsync()
{
    MRegForm.CreatorId = UserManager.GetUserId(User);

    var isAuthorized = await AuthorizationService.AuthorizeAsync(User, MRegForm, RegFormOperations.Create);
    if (!isAuthorized.Succeeded)
        return Forbid();

    // 检查是否存在重复的FirstName+LastName+DOB组合(忽略时间部分)
    var exists = await Context.MRegForms
        .AnyAsync(m => m.FirstName == MRegForm.FirstName 
                     && m.LastName == MRegForm.LastName 
                     && m.DOB.Date == MRegForm.DOB.Date);

    if (exists)
    {
        ModelState.AddModelError(string.Empty, "已存在相同姓名和出生日期的记录,请检查后重试。");
        return Page();
    }

    Context.MRegForms.Add(MRegForm);
    try
    {
        await Context.SaveChangesAsync();
    }
    catch (DbUpdateException ex)
    {
        // 捕获并发场景下的数据库唯一约束异常(SQL Server错误码2601/2627)
        if (ex.InnerException is SqlException sqlEx && (sqlEx.Number == 2601 || sqlEx.Number == 2627))
        {
            ModelState.AddModelError(string.Empty, "已存在相同姓名和出生日期的记录,请检查后重试。");
            return Page();
        }
        throw;
    }

    return RedirectToPage("./Index");
}

修改编辑方法的OnPostAsync

编辑时需要排除当前记录本身(编辑自身信息不算重复):

public async Task<IActionResult> OnPostAsync(int id)
{
    var mRegForm = await Context.MRegForms.AsNoTracking().SingleOrDefaultAsync(m => m.MRegFormId == id);
    if (mRegForm == null)
        return NotFound();

    MRegForm.CreatorId = mRegForm.CreatorId;
    MRegForm.Status = mRegForm.Status;

    var isAuthorized = await AuthorizationService.AuthorizeAsync(User, MRegForm, RegFormOperations.Update);
    if (!isAuthorized.Succeeded)
        return Forbid();

    // 检查是否存在其他记录有相同的FirstName+LastName+DOB组合
    var exists = await Context.MRegForms
        .AnyAsync(m => m.MRegFormId != id 
                     && m.FirstName == MRegForm.FirstName 
                     && m.LastName == MRegForm.LastName 
                     && m.DOB.Date == MRegForm.DOB.Date);

    if (exists)
    {
        ModelState.AddModelError(string.Empty, "已存在相同姓名和出生日期的记录,请检查后重试。");
        return Page();
    }

    Context.Attach(MRegForm).State = EntityState.Modified;
    try
    {
        await Context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!MRegFormExists(MRegForm.MRegFormId))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    catch (DbUpdateException ex)
    {
        if (ex.InnerException is SqlException sqlEx && (sqlEx.Number == 2601 || sqlEx.Number == 2627))
        {
            ModelState.AddModelError(string.Empty, "已存在相同姓名和出生日期的记录,请检查后重试。");
            return Page();
        }
        throw;
    }

    return RedirectToPage("./Index");
}

private bool MRegFormExists(int id)
{
    return Context.MRegForms?.Any(e => e.MRegFormId == id) ?? false;
}

3. 在前端页面显示错误提示

在Create.cshtml和Edit.cshtml的表单上方添加错误显示区域:

@if (!ModelState.IsValid)
{
    <div class="alert alert-danger">
        @foreach (var error in ModelState.Values.SelectMany(v => v.Errors))
        {
            <p>@error.ErrorMessage</p>
        }
    </div>
}

关键说明

  • 同时做业务检查和异常捕获是为了覆盖并发场景:可能在检查后、保存前有其他请求插入了重复数据,此时数据库约束会触发异常,需要捕获处理。
  • 使用DOB.Date匹配是为了忽略时分秒的差异,若需要精确到时间,可去掉.Date。
  • 不同数据库的唯一约束错误码不同,MySQL为1062,需根据实际使用的数据库调整判断逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 19:35:18