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?


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)
    
  • User Avatar
    0
    alirizaadiyahsi created

    It worked. Thanks @maliming.

Made with ❤️ on ABP v9.1.0-preview. Updated on December 05, 2024, 12:19