Activities of "O_SAG"

Thank you. You have been very helpful.

I don't understand; you can use the same DB container instead of create a new DB container.

Those are unfortunately our requirements: We have to be able to host every tenant db on a separate server, which means every tenant db needs to be a separate container.

you don't need to do it; ABP will publish a tenant-created event, and the handler will create a database.

Ok. So we could modify HandleEventAsync() of this handler to call a script on the server to create the docker container and only then apply the migration?

Thank you for your answers. We have more questions:

We want to automate the tenant creation process. Since we want our tenant dbs in docker containers, before the tenant db is created and seeded we need to create the docker container (with a script executed on the server), and only then we know what the connection string for the tenant will look like.

As a second step, we plan to execute the DbMigrator to apply migrations and seeding data to this newly created tenant db container.

Is there a way to pass seeding data to the DbMigrator when executing it with dotnet run? Asked another way: How does the DbMigrator get the admin credentials of a newly created tenant if it is executed separately with dotnet run?

Or does it make more sense to inject the DbMigrator Service in the backend and execute it that way instead?

Sorry, but this does not answer my questions at all.

I was first of all asking for a general guide, a summary or something else concerning a multi-tenant architecure with separate tenant dbs (for example in docker containers on another server than the host db). I believe we would not be the only ones working with abp who would profit from having such a guide, since the documentation is lacking in this topic.


Secondly, it seems you are talking about a out-of-the box abp commercial solution with the saas module. We are aware of this. This was not the question, however.

We need to know how to get to this point with our architecture. There needs to happen some setup for what you said to work with separate tenant dbs.

Questions:

  • Do we need a 2. DbContext for all TenantDBs (with MultiTenancySide=Tenant)?
  • Does the DbMigrator support migrations and data seeding without extra modification for a multi-tenant architecure with separate tenant dbs? (assuming everything else is correctly set up)
  • How do we customize the seeding data for every tenant (like, a admin user with a certain password)?
  • If running the DbMigrator separately (with dotnet run), how does the DbMigrator find out which tenants are new and need to be setup and data seeded?
  • ABP Framework version: v8
  • UI Type: Angular
  • Database System: EF Core (SQL Server, Oracle, MySQL, PostgreSQL, etc..)
  • Tiered (for MVC) or Auth Server Separated (for Angular): yes

Hello,

we are trying to implement an architecture with separate tenant dbs.

However, the docs in this regard are lacking. We could not find anything really useful to us that ties everything together and explains it. Multi-tenancy with separate dbs is mentioned in multiple places, but never really explained fully.

Doc pages which mention it (but only briefly or only one aspect of it):

  • https://abp.io/docs/latest/framework/data/entity-framework-core/migrations#separating-host-tenant-database-schemas
  • https://abp.io/docs/latest/modules/saas
  • https://abp.io/docs/latest/modules/tenant-management this references the saas module docs which explains nothing
  • https://abp.io/docs/latest/framework/architecture/multi-tenancy
  • https://abp.io/docs/latest/framework/fundamentals/connection-strings this references the multi-tenancy document which explains nothing for our problem

Could you please provide a general guide on multi-tenancy with separate tenant dbs? Especially in regards to the DbMigrator, which we could not get working at all.

Thank you, it works now. I must have confused the packages.

  • ABP Framework version: v8.0.3
  • UI Type: Angular
  • Database System: EF Core (PostgreSQL)
  • Tiered (for MVC) or Auth Server Separated (for Angular): yes
  • Exception message and full stack trace: DependencyResolutionException: None of the constructors found on type 'Volo.Abp.Account.Web.Pages.Account.OpenIddictSupportedLoginModel' can be invoked with the available services and parameters: Cannot resolve parameter 'Volo.Abp.Account.ExternalProviders.IAccountExternalProviderAppService accountExternalProviderAppService' of constructor 'Void .ctor(Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider, Microsoft.Extensions.Options.IOptions1[Volo.Abp.Account.Public.Web.AbpAccountOptions], Volo.Abp.Account.Security.Recaptcha.IAbpRecaptchaValidatorFactory, Volo.Abp.Account.ExternalProviders.IAccountExternalProviderAppService, Volo.Abp.Security.Claims.ICurrentPrincipalAccessor, Microsoft.Extensions.Options.IOptions1[Microsoft.AspNetCore.Identity.IdentityOptions], Microsoft.Extensions.Options.IOptionsSnapshot`1[Owl.reCAPTCHA.reCAPTCHAOptions], Volo.Abp.OpenIddict.AbpOpenIddictRequestHelper)'.

DependencyResolutionException: An exception was thrown while activating Volo.Abp.Account.Web.Pages.Account.OpenIddictSupportedLoginModel.

Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context)
Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext context)
Autofac.Core.Resolving.Middleware.RegistrationPipelineInvokeMiddleware.Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context)
Autofac.Core.Resolving.Middleware.SharingMiddleware.Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context)
Autofac.Core.Resolving.Middleware.ScopeSelectionMiddleware.Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context)
Autofac.Core.Resolving.Middleware.CircularDependencyDetectorMiddleware.Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context)
Autofac.Extensions.DependencyInjection.KeyedServiceMiddleware.Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context)
Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext context)
Autofac.Core.Resolving.ResolveOperation.InvokePipeline(ref ResolveRequest request, DefaultResolveRequestContext requestContext)
Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ref ResolveRequest request)
Autofac.Core.Resolving.ResolveOperation.ExecuteOperation(ref ResolveRequest request)
Autofac.Core.Resolving.ResolveOperation.Execute(ref ResolveRequest request)
Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(ref ResolveRequest request)
Autofac.Core.Lifetime.LifetimeScope.Autofac.IComponentContext.ResolveComponent(ref ResolveRequest request)
Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable<Parameter> parameters, out object instance)
Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable<Parameter> parameters)
Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable<Parameter> parameters)
Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType)
Autofac.Extensions.DependencyInjection.AutofacServiceProvider.GetRequiredService(Type serviceType)
Volo.Abp.AspNetCore.Mvc.UI.RazorPages.ServiceBasedPageModelActivatorProvider+<>c__DisplayClass0_0.<CreateActivator>b__0(PageContext context)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.DefaultPageModelFactoryProvider+<>c__DisplayClass3_0.<CreateModelFactory>b__0(PageContext pageContext)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.CreateInstance()
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ExceptionContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeNextResourceFilter()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|7_0(Endpoint endpoint, Task requestTask, ILogger logger)
Volo.Abp.AspNetCore.Serilog.AbpSerilogMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext()
Volo.Abp.AspNetCore.Auditing.AbpAuditingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
Volo.Abp.AspNetCore.Auditing.AbpAuditingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext()
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Volo.Abp.AspNetCore.Security.Claims.AbpDynamicClaimsMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext()
Volo.Abp.AspNetCore.Uow.AbpUnitOfWorkMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext()
Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext()
Volo.Abp.AspNetCore.MultiTenancy.MultiTenancyMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext()
Microsoft.AspNetCore.Builder.ApplicationBuilderAbpOpenIddictMiddlewareExtension+<>c__DisplayClass0_0+<<UseAbpOpenIddictValidation>b__0>d.MoveNext()
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Volo.Abp.AspNetCore.Tracing.AbpCorrelationIdMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext()
Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.RequestLocalization.AbpRequestLocalizationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext()
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
  • Steps to reproduce the issue: unsure

Following up on the previous two problems with migration to commercial:

https://support.abp.io/QA/Questions/7345/Dependency-Injection-Exception-after-migration-from-open-source-to-commercial https://support.abp.io/QA/Questions/7398/DbMigrator-fails-silently-after-migration-to-abp-commercial

We have the exception seen above when logging in with the AuthServer Login UI (the AuthServer is standalone).

If I understand the error correctly, then IAccountExternalProviderAppService needs to be injected but is missing. This page https://docs.abp.io/api-docs/commercial/3.3/api/Volo.Abp.Account.ExternalProviders.AccountExternalProviderAppService.html says its part of theVolo.Abp.Account.Pro.Public.Application package, and it is installed.

How do I provide this Interface to the ServiceProvider? Or did I misunderstand the error?

You were right. I solved it. AbpLicenceCode in appsettings.secrets.json was not read in, because we added custom code to read in appsettings from different environments, which did not read in the secrets file since we did not need it before.

Now that the DbMigrator has the Licence Code, it does not crash anymore.

Maybe a suggestion: There should be an error message thrown, its really confusing and inconvenient if the program silently crashes.

Thank you for your help @liangshiwei

Further investigations:

  • dotnet build works without issues
  • The error occures in await application.InitializeAsync(); in StartAsync() in DbMigratorHostedService.cs:
public async Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Starting DbMigrator...");
        using var application = await AbpApplicationFactory.CreateAsync<CapDbMigratorModule>(options =>
        {
            options.Services.ReplaceConfiguration(_configuration);
            options.Services.ReplaceConfiguration(BuildConfiguration());
            options.UseAutofac();
            options.Services.AddLogging(c => c.AddSerilog());
            options.AddDataMigrationEnvironment();
        });
        
        Console.WriteLine("0");
        
        await application.InitializeAsync();
        
        Console.WriteLine("1");

        await application
            .ServiceProvider
            .GetRequiredService<CapDbMigrationService>()
            .MigrateAsync();
        
        Console.WriteLine("2");

        await application.ShutdownAsync();
        
        Console.WriteLine("3");

        _hostApplicationLifetime.StopApplication();
        Console.WriteLine("DbMigrator finished.");
    }

It does not reach Console.WriteLine("1");. Now it comes to abp internals of which I have no idea. Maybe this helps?

You can try configure the appsettings.secrets.json

Thanks, but this is already implemented like you suggested. I'm not sure appsettings.secrets.json is even the problem :(

Showing 1 to 10 of 13 entries
Made with ❤️ on ABP v9.0.0-preview Updated on September 19, 2024, 10:13