Activities of "alirizaadiyahsi"

Here is my original question: https://support.abp.io/QA/Questions/4883/Dynamically-add-controller

This is working with a simple controller. But if I inject transient dependency classes, it is only working for the first request after that I am getting following error:

System.ObjectDisposedException: 'Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.

Here is my controller (as string):

public class DbProviderController : ControllerBase
{
    string connectionString = "some connection string";
    private readonly DaisyDatabaseProviderFactory _daisyDatabaseProviderFactory;

    public DbProviderController(DaisyDatabaseProviderFactory daisyDatabaseProviderFactory)
    {
        _daisyDatabaseProviderFactory = daisyDatabaseProviderFactory;
    }

    [HttpGet]
    public async Task<IEnumerable<Dictionary<string, object>>> GetPatientData()
    {
        return await _daisyDatabaseProviderFactory.Create(Daisy.Core.DatabaseProviders.DatabaseProviderTypes.MsSql).GetAllAsync(connectionString, "select * from some_table", 200);
    }
}

I am compiling this controller code string and adding as appPart like this:

public class DynamicApplicationPartManager : ITransientDependency
{
    private readonly ApplicationPartManager _partManager;
    private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;

    public DynamicApplicationPartManager(ApplicationPartManager partManager, IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
    {
        _partManager = partManager;
        _actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
    }

    public void Create(byte[] assembly)
    {
        var newAssembly = Assembly.Load(assembly);
        if (_partManager.ApplicationParts.Any(ap => ap.Name == newAssembly.GetName().Name)) return;

        _partManager.ApplicationParts.Add(new AssemblyPart(newAssembly));

        DaisyActionDescriptorChangeProvider.Instance.HasChanged = true;
        DaisyActionDescriptorChangeProvider.Instance.TokenSource.Cancel();
    }
    
    ...
}

DaisyDatabaseProviderFactory :

public class DaisyDatabaseProviderFactory : ITransientDependency
{
    private readonly IServiceProvider _serviceProvider;

    public DaisyDatabaseProviderFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IDaisyDatabaseProvider Create(DatabaseProviderTypes databaseProvider)
    {
        return databaseProvider switch
        {
            DatabaseProviderTypes.MsSql => _serviceProvider.GetRequiredService<MsSqlDatabaseProvider>(),
            DatabaseProviderTypes.MySql => _serviceProvider.GetRequiredService<MySqlDatabaseProvider>(),
            DatabaseProviderTypes.PostgreSql => _serviceProvider.GetRequiredService<PostgreSqlDatabaseProvider>(),
            DatabaseProviderTypes.MongoDb => _serviceProvider.GetRequiredService<MongoDatabaseProvider>(),
            DatabaseProviderTypes.None => throw new NotImplementedException($"The database provider '{databaseProvider}' is not implemented!"),
            DatabaseProviderTypes.Oracle => throw new NotImplementedException($"The database provider '{databaseProvider}' is not implemented!"),
            DatabaseProviderTypes.SqLite => throw new NotImplementedException($"The database provider '{databaseProvider}' is not implemented!"),
            _ => throw new NotImplementedException($"The database provider '{databaseProvider}' is not implemented!")
        };
    }
}

MsSqlDatabaseProvider

public class MsSqlDatabaseProvider : IDaisyDatabaseProvider
{
    public async Task<IEnumerable<Dictionary<string, object>>> GetAllAsync(string connectionString, string query, int maxResultCount = int.MaxValue, int skipCount = 0)
    {
        await using var connection = new SqlConnection(connectionString);
        query = $"SELECT * FROM ({query}) AS t order by 1 OFFSET {skipCount} ROWS FETCH NEXT {maxResultCount} ROWS ONLY";
        var rows = await connection.QueryAsync(query, commandTimeout: 60 * 3); 

        return rows.ToDictionaryRowsList();
    }
}

IDaisyDatabaseProvider

public interface IDaisyDatabaseProvider : ITransientDependency
{
    Task<IEnumerable<Dictionary<string, object>>> GetAllAsync(string connectionString, string query, int maxResultCount = int.MaxValue, int skipCount = 0);
}

ApiManagerServiceHttpApiModule

[DependsOn(
    typeof(ApiManagerServiceApplicationContractsModule),
    typeof(AbpAspNetCoreMvcModule),
    typeof(CoreHttpApiModule))]
public class ApiManagerServiceHttpApiModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        PreConfigure<IMvcBuilder>(mvcBuilder => { mvcBuilder.AddApplicationPartIfNotExists(typeof(ApiManagerServiceHttpApiModule).Assembly); });
    }

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddSingleton<IActionDescriptorChangeProvider>(DaisyActionDescriptorChangeProvider.Instance);
        context.Services.AddSingleton(DaisyActionDescriptorChangeProvider.Instance);

        context.Services.AddTransient<ServiceBasedControllerActivator>();
        context.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, DaisyControllerActivator>());

        Configure<AbpLocalizationOptions>(options =>
        {
            options.Resources
                .Get<ApiManagerServiceResource>()
                .AddBaseTypes(typeof(AbpUiResource));
        });

        Configure<AbpAuditingOptions>(options =>
        {
            options.IsEnabledForGetRequests = true;
            options.HideErrors = false;
            options.IsEnabledForIntegrationServices = true;
        });
        
        Configure<AbpExceptionHandlingOptions>(options =>
        {
            options.SendExceptionsDetailsToClients = true;
            options.SendStackTraceToClients = false;
        });

        context.Services.ConfigureOpenTelemetry();
    }

    public override async Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context)
    {
        await context.ServiceProvider
            .GetRequiredService<ApiManagerControllerRegistrar>()
            .RegisterAsync();
    }
}

And after that I can see it in swagger. For the first request it is working, other than that it is throwing the error that I mentioned above:

[api-manager-service_d2c3d4bb-2]: [09:16:49 ERR] ---------- RemoteServiceErrorInfo ----------
[api-manager-service_d2c3d4bb-2]: {
[api-manager-service_d2c3d4bb-2]: "code": null,
[api-manager-service_d2c3d4bb-2]: "message": "Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.",
[api-manager-service_d2c3d4bb-2]: "details": "ObjectDisposedException: Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.\r\n",
[api-manager-service_d2c3d4bb-2]: "data": {},
[api-manager-service_d2c3d4bb-2]: "validationErrors": null
[api-manager-service_d2c3d4bb-2]: }
[api-manager-service_d2c3d4bb-2]:
[api-manager-service_d2c3d4bb-2]: [09:16:49 ERR] Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.
[api-manager-service_d2c3d4bb-2]: System.ObjectDisposedException: Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.
[api-manager-service_d2c3d4bb-2]: at Autofac.Core.Lifetime.LifetimeScope.ThrowDisposedException()
[api-manager-service_d2c3d4bb-2]: at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(ResolveRequest request)
[api-manager-service_d2c3d4bb-2]: at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
[api-manager-service_d2c3d4bb-2]: at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
[api-manager-service_d2c3d4bb-2]: at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
[api-manager-service_d2c3d4bb-2]: at Daisy.Core.DataManagement.DatabaseProviders.DaisyDatabaseProviderFactory.Create(DatabaseProviderTypes databaseProvider) in C:\Users\aliriza\Documents\Projects\GitHub\Daisy\shared\Daisy.Core\src\Daisy.Core.Domain\DataManagement\DatabaseProviders\DaisyDatabaseProviderFactory.cs:line 25
[api-manager-service_d2c3d4bb-2]: at DaisyDataBaseProvider.DbProviderController.GetPatientData()
[api-manager-service_d2c3d4bb-2]: at lambda_method2045(Closure, Object)
[api-manager-service_d2c3d4bb-2]: at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
[api-manager-service_d2c3d4bb-2]: at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
[api-manager-service_d2c3d4bb-2]: at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
[api-manager-service_d2c3d4bb-2]: at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
[api-manager-service_d2c3d4bb-2]: at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
[api-manager-service_d2c3d4bb-2]: at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
[api-manager-service_d2c3d4bb-2]: --- End of stack trace from previous location ---
[api-manager-service_d2c3d4bb-2]: at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

BTW, it is working if I write this controller hardcoded (traditional way).

  • ABP Framework version: v7.1.0
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): Microservice

I have following code to add controller dynamically.

    [HttpPost]
    public async Task<AppPartDto> CreateAsync(CreateAppPartInput input)
    {
        var dto = await _appPartsAppService.CreateAsync(input);
        if (!dto.IsRunning) return dto;

        var assembly = Assembly.Load(dto.CompiledCode);
        if (_partManager.ApplicationParts.Any(ap => ap.Name == assembly.GetName().Name)) return dto;
        _partManager.ApplicationParts.Add(new AssemblyPart(assembly));
        DaisyActionDescriptorChangeProvider.Instance.HasChanged = true;
        DaisyActionDescriptorChangeProvider.Instance.TokenSource.Cancel();

        return dto;
    }

And I can see the controller is added in swagger:

But when I want to call it, I am getting following error.

[api-manager-service_87ede53f-7]: [19:18:00 ERR] ---------- RemoteServiceErrorInfo ----------
[api-manager-service_87ede53f-7]: {
[api-manager-service_87ede53f-7]: "code": null,
[api-manager-service_87ede53f-7]: "message": "An internal error occurred during your request!",
[api-manager-service_87ede53f-7]: "details": null,
[api-manager-service_87ede53f-7]: "data": {},
[api-manager-service_87ede53f-7]: "validationErrors": null
[api-manager-service_87ede53f-7]: }
[api-manager-service_87ede53f-7]:
[api-manager-service_87ede53f-7]: [19:18:00 ERR] The requested service 'Daisy.ApiManagerService.Customers.CustomersController' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
[api-manager-service_87ede53f-7]: Autofac.Core.Registration.ComponentNotRegisteredException: The requested service 'Daisy.ApiManagerService.Customers.CustomersController' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
[api-manager-service_87ede53f-7]: at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
[api-manager-service_87ede53f-7]: at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.&lt;&gt;c__DisplayClass6_0.&lt;CreateControllerFactory&gt;g__CreateController|0(ControllerContext controllerContext)
[api-manager-service_87ede53f-7]: at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
[api-manager-service_87ede53f-7]: at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
[api-manager-service_87ede53f-7]: --- End of stack trace from previous location ---
[api-manager-service_87ede53f-7]: at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeNextExceptionFilterAsync&gt;g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
[api-manager-service_87ede53f-7]: [19:18:00 INF] Executing ObjectResult, writing value of type 'Volo.Abp.Http.RemoteServiceErrorResponse'.
[api-manager-service_87ede53f-7]: [19:18:00 INF] Executed action Daisy.ApiManagerService.Customers.CustomersController.Get (OnTheFlyCompilationAssembly) in 237.6787ms
[api-manager-service_87ede53f-7]: [19:18:00 INF] Executed endpoint 'Daisy.ApiManagerService.Customers.CustomersController.Get (OnTheFlyCompilationAssembly)'
[api-manager-service_87ede53f-7]: [19:18:00 DBG] Added 0 entity changes to the current audit log
[api-manager-service_87ede53f-7]: [19:18:00 INF] Request finished HTTP/2 GET https://localhost:44996/api/api-manager-service/customers - - - 500 - application/json;+charset=utf-8 240.8945ms

Maybe there is an elegant way to add controller dynamically?

I can do this in an dotnet api project (which is not related to ABP) and it is working. But with ABP I think I should register the assembly(controller) to dependency container dynamically.

Any suggestion would be appropriate. Thanks.

  • ABP Framework version: v5.3.0
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): Microservice

I have following two entities:

public class FunctionalPosition : FullAuditedAggregateRoot<Guid>, IMultiTenant and public class AccessRequest : FullAuditedEntity<Guid>, IMultiTenant

DomainService for FunctionalPosition

DomainService for AccessRequest

FunctionalPosition Table

AccessRequest Table

FunctionalPosition and AccessRequest are in the same microservice and DB, but there is wrong with AccessRequest to set user and tenant. Also I am checking by debugging both client and server has the current tenant/user information. Am I missing something?

  • ABP Framework version: v5.3.0
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): Angular Microservice

I added a new microservice that named FileManagerService and added database blob storing to it by using this command: abp add-module Volo.Abp.BlobStoring.Database.

All looks fine but when I trying to save files, provider is using AdminstrationServiceDB.

[Authorize(FileManagerServicePermissions.FileManager.Files.FilesDefault)]
public class FileAppService : ApplicationService, IFileAppService
{
    private IBlobContainer _fileContainer;
    private readonly IBlobContainerFactory _blobContainerFactory;

    public FileAppService(IBlobContainerFactory blobContainerFactory)
    {
        _blobContainerFactory = blobContainerFactory;
    }

    public async Task&lt;BlobDto&gt; GetBlobAsync(GetBlobRequestDto input)
    {
        var blob = await _fileContainer.GetAllBytesAsync(input.Name);

        return new BlobDto
        {
            Name = input.Name,
            Content = blob
        };
    }

    [Authorize(FileManagerServicePermissions.FileManager.Files.Create)]
    [Authorize(FileManagerServicePermissions.FileManager.Files.Edit)]
    public async Task SaveBlobAsync(CreateBlobInput input)
    {
        _fileContainer = _blobContainerFactory.Create("microservice-name");
        await _fileContainer.SaveAsync(input.Name, input.Content, true);
    }

How I configure blobContainer to use file-manager connection string, instead of using administration-service connection string?

And this is the file-manager-service connection strings in appsettings.json.

And DB

  • 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&lt;AccessRequestApprovedEto&gt;, ITransientDependency
{
    private readonly IdentityUserManager _userManager;
    private readonly ICustomIdentityRoleRepository _customIdentityRoleRepository;
    private readonly ILogger&lt;AccessRequestApprovedHandler&gt; _logger;

    public AccessRequestApprovedHandler(IdentityUserManager userManager, ICustomIdentityRoleRepository customIdentityRoleRepository, ILogger&lt;AccessRequestApprovedHandler&gt; 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&lt;List&lt;IdentityRole&gt;> 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?

  • ABP Framework version: v5.2.2
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): no, Microservice

Identity API

I am customizing Identity module according to this document: Customizing the Application Modules I extended IdentityRoles service/repository/contoller so I have a method _roleService.GetListByIds(Guid[] ids). Everything is ok, so far. The end-point is working like expected.

Identity Angular

Question: I am creating ng-library for my each microservice to generate proxies, separately (reference:Microservice Proxy Generation). To use my new method that is in IdentityRoleService, should I create an ng-library for identity and generate identity proxies in it? So, as I understand, I will use these proxies instead of using them from abp/ng.core?

Any suggestion would be appreciated! Thanks...

Showing 1 to 6 of 6 entries
Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11