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

如何在ASP.NET Core Identity中通过事务实现用户角色修改?

报错原因

该报错核心有两个诱因:

  1. 使用TransactionScope时未开启异步流支持,异步操作的上下文无法被事务正确感知,导致后续操作拿到的数据库连接不属于当前事务范围。
  2. 原有代码使用.Result同步阻塞异步方法,导致线程上下文切换混乱,不同步的操作尝试占用已被事务绑定的连接。

推荐解决方案:使用EF Core本地事务

ASP.NET Core Identity默认基于EF Core实现存储,直接复用Identity对应的DbContext开启事务即可,不需要额外依赖,适配性最好:

// 类中先注入依赖
private readonly UserManager<User> _userManager;
private readonly ApplicationDbContext _dbContext;

// 构造函数注入实例
public YourService(UserManager<User> userManager, ApplicationDbContext dbContext)
{
    _userManager = userManager;
    _dbContext = dbContext;
}

// 改为异步方法,全程使用await避免阻塞
public async Task UpdateRolesAsync(long userId, List<string> roles)
{
    // 开启EF Core本地事务
    using var transaction = await _dbContext.Database.BeginTransactionAsync();
    try
    {
        var user = await _userManager.FindByIdAsync(userId.ToString());
        if (user == null)
        {
            throw new Exception("指定用户不存在");
        }
        
        var existingRoles = (await _userManager.GetRolesAsync(user)).ToList();
        var newRoles = roles.Where(i => !existingRoles.Contains(i)).ToList();
        var rolesToBeDeleted = existingRoles.Where(i => !roles.Contains(i)).ToList();

        if (rolesToBeDeleted.Any())
        {
            var removeResult = await _userManager.RemoveFromRolesAsync(user, rolesToBeDeleted);
            if (!removeResult.Succeeded)
            {
                throw new Exception($"移除用户角色失败: {removeResult.Serialize()}");
            }
        }

        if (newRoles.Any())
        {
            var addResult = await _userManager.AddToRolesAsync(user, newRoles);
            if (!addResult.Succeeded)
            {
                throw new Exception($"添加用户角色失败: {addResult.Serialize()}");
            }
        }

        // 所有操作执行成功,提交事务
        await transaction.CommitAsync();
    }
    catch
    {
        // 出现异常回滚所有修改
        await transaction.RollbackAsync();
        throw;
    }
}

备选方案:使用TransactionScope

如果业务必须使用TransactionScope,只需要添加异步流支持参数,并且全程使用异步调用即可解决报错:

public async Task UpdateRolesAsync(long userId, List<string> roles)
{
    // 传入TransactionScopeAsyncFlowOption.Enabled开启异步上下文事务传播
    using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
    
    var user = await _userManager.FindByIdAsync(userId.ToString());
    if (user == null)
    {
        throw new Exception("指定用户不存在");
    }
    
    var existingRoles = (await _userManager.GetRolesAsync(user)).ToList();
    var newRoles = roles.Where(i => !existingRoles.Contains(i)).ToList();
    var rolesToBeDeleted = existingRoles.Where(i => !roles.Contains(i)).ToList();

    if (rolesToBeDeleted.Any())
    {
        var removeResult = await _userManager.RemoveFromRolesAsync(user, rolesToBeDeleted);
        if (!removeResult.Succeeded)
        {
            throw new Exception($"移除用户角色失败: {removeResult.Serialize()}");
        }
    }

    if (newRoles.Any())
    {
        var addResult = await _userManager.AddToRolesAsync(user, newRoles);
        if (!addResult.Succeeded)
        {
            throw new Exception($"添加用户角色失败: {addResult.Serialize()}");
        }
    }

    scope.Complete();
}

该方案需要注意:.NET Core 2.1及以上版本才支持TransactionScope的异步流配置,如果使用SQL Server且事务涉及多个不同连接,需要开启MSDTC服务支持分布式事务。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 22:54:04