Activities of "alirizaadiyahsi"

@maliming, not it is clear. As you said problem is MyTypeActivatorCache . Now I removed it and I am creating controller every time. Here is the latest controller activator:

public class DaisyControllerActivator : IControllerActivator
{
    private readonly ServiceBasedControllerActivator _serviceBasedControllerActivator;

    public DaisyControllerActivator(ServiceBasedControllerActivator serviceBasedControllerActivator)
    {
        _serviceBasedControllerActivator = serviceBasedControllerActivator;
    }

    public object Create(ControllerContext context)
    {
        try
        {
            return _serviceBasedControllerActivator.Create(context);
        }
        catch (Exception ex)
        {
            // Create controller if not found.
            return ActivatorUtilities.CreateInstance(context.HttpContext.RequestServices, context.ActionDescriptor.ControllerTypeInfo.AsType());
        }
    }

    public void Release(ControllerContext context, object controller)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }
        
        if (controller == null)
        {
            throw new ArgumentNullException(nameof(controller));
        }
        
        _serviceBasedControllerActivator.Release(context, controller);
    }
}

Thanks a lot for your patient/help.

Actually I shared all the code I have in the question.

Okay it is working now. Could give more detailed explanation about this?

Also, what is the best practice, should I always use IRootServiceProvider instead of IServiceProvider?

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).

Hi @liangshiwei, this is the good point. This fixes my issue.

Thanks a lot.

  • 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.

Hi @maliming another silly mistake :) I did not check after Rider created auto-generated code.

Thanks.

Hi @maliming, I removed blobStoring from project. We will no longer use this.

Thanks for your helping and your time.

  • 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?

Hi @maliming I can't understand what you mean. I have no connection issues, actually. Each microservice can connect to its own DB via repos without issue. The problem is blobProvider is bypassing fileManagerConnectionString by administration-service. Repositories that are in blobProvider is connecting the administration service DB.

I think I should configure blobStoringProvider to connect file-manager-service DB, but I can't find any solution about this.

BTW, here is my connections:

Showing 1 to 10 of 16 entries
Made with ❤️ on ABP v9.1.0-preview. Updated on November 01, 2024, 05:35