Open Closed

Using domain service class (e.g. IdentityUserManager) in DistributedEventHandler #3201


User avatar
0
alirizaadiyahsi created
  • ABP Framework version: v5.2.2
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): microservice/angular
EventHandler Implementation
public class AccessRequestApprovedHandler : IDistributedEventHandler<AccessRequestApprovedEto>, ITransientDependency
{
    private readonly IdentityUserManager _userManager;
    private readonly ICustomIdentityRoleRepository _customIdentityRoleRepository;
    private readonly ILogger<AccessRequestApprovedHandler> _logger;

    public AccessRequestApprovedHandler(IdentityUserManager userManager, ICustomIdentityRoleRepository customIdentityRoleRepository, ILogger<AccessRequestApprovedHandler> logger)
    {
        _userManager = userManager;
        _customIdentityRoleRepository = customIdentityRoleRepository;
        _logger = logger;
    }

    [UnitOfWork]
    public async Task HandleEventAsync(AccessRequestApprovedEto eventData)
    {
        try
        {
            var user = await _userManager.GetByIdAsync(eventData.UserId);
            var roles = await _customIdentityRoleRepository.GetListByIdsAsync(eventData.RoleIds);
            
            // Exception is thrown here.
            var identityResult = await _userManager.SetRolesAsync(user, roles.Select(r => r.Name).ToArray());
            if (!identityResult.Succeeded)
            {
                _logger.LogError($"Error occured while setting roles for user: {user.UserName}");
                foreach (var error in identityResult.Errors)
                {
                    _logger.LogError($"Error: {error.Code} - {error.Description}");
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }
}

Exception Message (even though I am using UnitOfWork attribute) : A DbContext can only be created inside a unit of work!

   at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider`1.GetDbContextAsync()
   at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository`2.EnsureCollectionLoadedAsync[TProperty](TEntity entity, Expression`1 propertyExpression, CancellationToken cancellationToken)
   at Volo.Abp.Domain.Repositories.RepositoryExtensions.EnsureCollectionLoadedAsync[TEntity,TKey,TProperty](IBasicRepository`2 repository, TEntity entity, Expression`1 propertyExpression, CancellationToken cancellationToken)
   at Volo.Abp.Identity.IdentityUserStore.AddToRoleAsync(IdentityUser user, String normalizedRoleName, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Identity.UserManager`1.AddToRolesAsync(TUser user, IEnumerable`1 roles)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
   at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at Volo.Abp.Identity.IdentityUserManager.SetRolesAsync(IdentityUser user, IEnumerable`1 roleNames)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
   at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at Daisy.IdentityService.EventHandlers.AccessRequests.AccessRequestApprovedHandler.HandleEventAsync(AccessRequestApprovedEto eventData) in C:\Users\aliriza\Documents\Projects\GitHub\Daisy\services\identity\src\Daisy.IdentityService.Application\EventHandlers\AccessRequests\AccessRequestApprovedHandler.cs:line 34

BTW, here is repo method implementation:

    public async Task<List<IdentityRole>> GetListByIdsAsync(Guid[] ids, CancellationToken cancellationToken = default)
    {
        var query = await GetQueryableAsync();
        query = query.Where(r => ids.Contains(r.Id)).OrderBy(x => x.Name);
        return await query.ToListAsync(cancellationToken);
    }

I think it is coming from here: UnitOfWorkDbContextProvider.cs. it seems it can not use the current unit of work.

IdentityUserManager - SetRolesAsync - AddToRolesAsync - userRoleStore.AddToRoleAsync - UserRepository.EnsureCollectionLoadedAsync - GetDbContextAsync() - _dbContextProvider.GetDbContextAsync()

UPDATE:

I also try IUnitOfWorkEnable interface or inherite from ApplicationService but no luck.

It is only working when I manage uow manually. Following is working:

public async Task HandleEventAsync(AccessRequestApprovedEto eventData)
    {
        _logger.LogInformation("Event name: {@eventName}. User id: {@userId}. Role ids: {@roleIds}", AccessRequestConsts.EventNames.Approved, eventData.UserId, eventData.RoleIds);
        using var uow = _unitOfWorkManager.Begin();
        var user = await _userManager.GetByIdAsync(eventData.UserId);
        var roles = await _customIdentityRoleRepository.GetListByIdsAsync(eventData.RoleIds);
        var identityResult = await _userManager.SetRolesAsync(user, roles.Select(r => r.Name).ToArray());
        if (!identityResult.Succeeded)
        {
            _logger.LogError($"Error occured while setting roles for user: {user.UserName}");
            foreach (var error in identityResult.Errors)
            {
                _logger.LogError($"Error: {error.Code} - {error.Description}");
            }
        }

        await uow.CompleteAsync();
    }

But I cant understand why [UnitOfWork] attribute is not working?

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

2 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You forgot the virtual.

     [UnitOfWork]
    public virtual async Task HandleEventAsync(AccessRequestApprovedEto eventData)
    
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    alirizaadiyahsi created

    It worked. Thanks @maliming.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.