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:
MultiTenancySide=Tenant
)?dotnet run
), how does the DbMigrator find out which tenants are new and need to be setup and data seeded?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):
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.
1[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.IOptions
1[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)
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 issuesawait 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?