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

ASP.NET Core Web API删除数据后自增主键不重用及手动插入报错问题

问题

在ASP.NET Core Web API项目中,使用HTTP Delete删除SQL数据库中的行后,插入新行时主键Id会自动递增并跳过已删除的Id值,如何避免这种情况?

示例数据

IdEmailUserNameFirstNameLastNamePhoneNumber
15Test2@gmail.comTest3Test4string12345678
26stringstringstringstring0

现有代码

删除接口

[HttpDelete]
public async Task<IActionResult> DeleteUser(int id)
{
    if(_dbContext.Users == null)
    {
        return NotFound(nameof(DeleteUser));
    }
    var user = await _dbContext.Users.FindAsync(id);
    if(user == null)
    {
        return NotFound($"{nameof(DeleteUser)}");
    }
    _dbContext.Users.Remove(user);
    await _dbContext.SaveChangesAsync();
    return Ok("Successfully deleted.");
}

新增接口

[HttpPost]
public async Task<ActionResult<User>> PostUser(User user)
{
    _dbContext.Users.Add(user);
    await _dbContext.SaveChangesAsync();
    return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}

遇到的错误

尝试手动插入Id值时,抛出错误:

当IDENTITY_INSERT设置为OFF时,不能为表中的标识列插入显式值。

解决方案

方法一:关闭自增,手动管理主键ID

直接取消主键的自增属性,自行维护ID的唯一性:

  1. 修改数据库表结构,移除Id列的IDENTITY自增配置(SQL Server环境)。
  2. 在EF Core中配置实体类,关闭自动值生成:
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>()
            .Property(u => u.Id)
            .ValueGeneratedNever(); // 取消EF自动生成主键值
    }
    
  3. 新增数据时,手动分配未被使用的ID:
    [HttpPost]
    public async Task<ActionResult<User>> PostUser(User user)
    {
        // 示例:获取当前最小的未使用ID
        var usedIds = await _dbContext.Users.Select(u => u.Id).ToListAsync();
        int newId = 1;
        while(usedIds.Contains(newId))
        {
            newId++;
        }
        user.Id = newId;
        
        _dbContext.Users.Add(user);
        await _dbContext.SaveChangesAsync();
        return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
    }
    
    注意:高并发场景下需添加事务或锁机制,防止ID重复冲突。

方法二:删除数据后重置标识种子(不推荐生产环境)

仅适合测试环境,删除后重置自增序列的起始值:
在删除接口中添加SQL命令,将标识种子重置为当前表中的最大ID:

[HttpDelete]
public async Task<IActionResult> DeleteUser(int id)
{
    if(_dbContext.Users == null)
    {
        return NotFound(nameof(DeleteUser));
    }
    var user = await _dbContext.Users.FindAsync(id);
    if(user == null)
    {
        return NotFound($"{nameof(DeleteUser)}");
    }
    _dbContext.Users.Remove(user);
    await _dbContext.SaveChangesAsync();
    // 重置标识种子为当前最大ID
    await _dbContext.Database.ExecuteSqlRawAsync("DBCC CHECKIDENT ('Users', RESEED, (SELECT ISNULL(MAX(Id),0) FROM Users))");
    return Ok("Successfully deleted.");
}

缺点:生产环境频繁操作会影响性能,高并发下仍可能出现ID重复。

方法三:启用IDENTITY_INSERT,复用已删除ID

临时开启标识列的手动插入权限,插入指定ID后再关闭:

[HttpPost]
public async Task<ActionResult<User>> PostUser(User user)
{
    // 开启IDENTITY_INSERT
    await _dbContext.Database.ExecuteSqlRawAsync("SET IDENTITY_INSERT Users ON");
    
    _dbContext.Users.Add(user);
    await _dbContext.SaveChangesAsync();
    
    // 关闭IDENTITY_INSERT
    await _dbContext.Database.ExecuteSqlRawAsync("SET IDENTITY_INSERT Users OFF");
    
    return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}

注意:

  • 同一时间只能对一个表开启IDENTITY_INSERT
  • 必须确保插入的ID未被占用,否则会触发主键冲突
  • 操作需要对应数据库权限

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 02:43:17