From 8c5e3cd30aeb34eff42a3c292a9f1a4d4b19355e Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 17 Dec 2021 21:39:19 +0800 Subject: [PATCH] Async configure and initialize the module. --- .../AbpApplicationBuilderExtensions.cs | 22 ++++ .../ServiceCollectionApplicationExtensions.cs | 17 +++ .../Volo/Abp/AbpApplicationBase.cs | 107 +++++++++++++++++- .../Volo/Abp/AbpApplicationCreationOptions.cs | 3 + .../Volo/Abp/AbpApplicationFactory.cs | 55 +++++++++ ...pApplicationWithExternalServiceProvider.cs | 10 ++ ...pApplicationWithInternalServiceProvider.cs | 7 ++ .../Volo.Abp.Core/Volo/Abp/IAbpApplication.cs | 6 + ...pApplicationWithExternalServiceProvider.cs | 13 +++ ...pApplicationWithInternalServiceProvider.cs | 13 +++ .../Volo/Abp/IOnApplicationInitialization.cs | 5 +- .../Volo/Abp/IOnApplicationShutdown.cs | 5 +- .../Volo/Abp/Modularity/AbpModule.cs | 43 +++++++ .../DefaultModuleLifecycleContributor.cs | 36 +++++- .../Volo/Abp/Modularity/IAbpModule.cs | 6 +- .../Modularity/IModuleLifecycleContributor.cs | 7 +- .../Volo/Abp/Modularity/IModuleManager.cs | 7 +- .../IOnPostApplicationInitialization.cs | 5 +- .../IOnPreApplicationInitialization.cs | 5 +- .../Abp/Modularity/IPostConfigureServices.cs | 4 +- .../Abp/Modularity/IPreConfigureServices.cs | 4 +- .../ModuleLifecycleContributorBase.cs | 14 ++- .../Volo/Abp/Modularity/ModuleManager.cs | 41 +++++++ .../Abp/AbpApplication_Initialize_Tests.cs | 82 +++++++++++++- .../ConfigurationAccessor_Tests.cs | 36 +++++- .../AutoServiceRegistration_Tests.cs | 15 +++ .../HybridServiceScopeFactory_Tests.cs | 31 +++++ .../Volo/Abp/Modularity/TestModuleBase.cs | 68 ++++++++++- 28 files changed, 647 insertions(+), 20 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs index 2e8b221b3c..b15167fa1d 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.AspNetCore.RequestLocalization; using Microsoft.Extensions.DependencyInjection; @@ -18,6 +19,27 @@ public static class AbpApplicationBuilderExtensions { private const string ExceptionHandlingMiddlewareMarker = "_AbpExceptionHandlingMiddleware_Added"; + public async static Task InitializeApplicationAsync([NotNull] this IApplicationBuilder app) + { + Check.NotNull(app, nameof(app)); + + app.ApplicationServices.GetRequiredService>().Value = app; + var application = app.ApplicationServices.GetRequiredService(); + var applicationLifetime = app.ApplicationServices.GetRequiredService(); + + applicationLifetime.ApplicationStopping.Register(() => + { + application.ShutdownAsync(); + }); + + applicationLifetime.ApplicationStopped.Register(() => + { + application.Dispose(); + }); + + await application.InitializeAsync(app.ApplicationServices); + } + public static void InitializeApplication([NotNull] this IApplicationBuilder app) { Check.NotNull(app, nameof(app)); diff --git a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs index 7748953cc4..ce4af2befa 100644 --- a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs +++ b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using JetBrains.Annotations; using Volo.Abp; using Volo.Abp.Modularity; @@ -22,4 +23,20 @@ public static class ServiceCollectionApplicationExtensions { return AbpApplicationFactory.Create(startupModuleType, services, optionsAction); } + + public async static Task AddApplicationAsync( + [NotNull] this IServiceCollection services, + [CanBeNull] Action optionsAction = null) + where TStartupModule : IAbpModule + { + return await AbpApplicationFactory.CreateAsync(services, optionsAction); + } + + public async static Task AddApplicationAsync( + [NotNull] this IServiceCollection services, + [NotNull] Type startupModuleType, + [CanBeNull] Action optionsAction = null) + { + return await AbpApplicationFactory.CreateAsync(startupModuleType, services, optionsAction); + } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs index 85b1989445..c6e86b4af8 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -46,7 +47,21 @@ public abstract class AbpApplicationBase : IAbpApplication services.AddCoreAbpServices(this, options); Modules = LoadModules(services, options); - ConfigureServices(); + + if (!options.ManualConfigureServices) + { + ConfigureServices(); + } + } + + public virtual async Task ShutdownAsync() + { + using (var scope = ServiceProvider.CreateScope()) + { + await scope.ServiceProvider + .GetRequiredService() + .ShutdownModulesAsync(new ApplicationShutdownContext(scope.ServiceProvider)); + } } public virtual void Shutdown() @@ -70,6 +85,17 @@ public abstract class AbpApplicationBase : IAbpApplication ServiceProvider.GetRequiredService>().Value = ServiceProvider; } + protected virtual async Task InitializeModulesAsync() + { + using (var scope = ServiceProvider.CreateScope()) + { + WriteInitLogs(scope.ServiceProvider); + await scope.ServiceProvider + .GetRequiredService() + .InitializeModulesAsync(new ApplicationInitializationContext(scope.ServiceProvider)); + } + } + protected virtual void InitializeModules() { using (var scope = ServiceProvider.CreateScope()) @@ -111,7 +137,84 @@ public abstract class AbpApplicationBase : IAbpApplication } //TODO: We can extract a new class for this - protected virtual void ConfigureServices() + public virtual async Task ConfigureServicesAsync() + { + var context = new ServiceConfigurationContext(Services); + Services.AddSingleton(context); + + foreach (var module in Modules) + { + if (module.Instance is AbpModule abpModule) + { + abpModule.ServiceConfigurationContext = context; + } + } + + //PreConfigureServices + foreach (var module in Modules.Where(m => m.Instance is IPreConfigureServices)) + { + try + { + await ((IPreConfigureServices)module.Instance).PreConfigureServicesAsync(context); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during {nameof(IPreConfigureServices.PreConfigureServicesAsync)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } + } + + var assemblies = new HashSet(); + + //ConfigureServices + foreach (var module in Modules) + { + if (module.Instance is AbpModule abpModule) + { + if (!abpModule.SkipAutoServiceRegistration) + { + var assembly = module.Type.Assembly; + if (!assemblies.Contains(assembly)) + { + Services.AddAssembly(assembly); + assemblies.Add(assembly); + } + } + } + + try + { + await module.Instance.ConfigureServicesAsync(context); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServicesAsync)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } + } + + //PostConfigureServices + foreach (var module in Modules.Where(m => m.Instance is IPostConfigureServices)) + { + try + { + await ((IPostConfigureServices)module.Instance).PostConfigureServicesAsync(context); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during {nameof(IPostConfigureServices.PostConfigureServicesAsync)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } + } + + foreach (var module in Modules) + { + if (module.Instance is AbpModule abpModule) + { + abpModule.ServiceConfigurationContext = null; + } + } + } + + //TODO: We can extract a new class for this + public virtual void ConfigureServices() { var context = new ServiceConfigurationContext(Services); Services.AddSingleton(context); diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs index ea2d69aae6..8bf7579843 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs @@ -19,10 +19,13 @@ public class AbpApplicationCreationOptions [NotNull] public AbpConfigurationBuilderOptions Configuration { get; } + public bool ManualConfigureServices { get; set; } + public AbpApplicationCreationOptions([NotNull] IServiceCollection services) { Services = Check.NotNull(services, nameof(services)); PlugInSources = new PlugInSourceList(); Configuration = new AbpConfigurationBuilderOptions(); + ManualConfigureServices = false; } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationFactory.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationFactory.cs index 13b886f1c3..d904c24ca2 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationFactory.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationFactory.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; @@ -7,6 +8,60 @@ namespace Volo.Abp; public static class AbpApplicationFactory { + public async static Task CreateAsync( + [CanBeNull] Action optionsAction = null) + where TStartupModule : IAbpModule + { + var app = Create(typeof(TStartupModule), options => + { + optionsAction?.Invoke(options); + options.ManualConfigureServices = true; + }); + await app.ConfigureServicesAsync(); + return app; + } + + public async static Task CreateAsync( + [NotNull] Type startupModuleType, + [CanBeNull] Action optionsAction = null) + { + var app = new AbpApplicationWithInternalServiceProvider(startupModuleType, options => + { + optionsAction?.Invoke(options); + options.ManualConfigureServices = true; + }); + await app.ConfigureServicesAsync(); + return app; + } + + public async static Task CreateAsync( + [NotNull] IServiceCollection services, + [CanBeNull] Action optionsAction = null) + where TStartupModule : IAbpModule + { + var app = Create(typeof(TStartupModule), services, options => + { + optionsAction?.Invoke(options); + options.ManualConfigureServices = true; + }); + await app.ConfigureServicesAsync(); + return app; + } + + public async static Task CreateAsync( + [NotNull] Type startupModuleType, + [NotNull] IServiceCollection services, + [CanBeNull] Action optionsAction = null) + { + var app = new AbpApplicationWithExternalServiceProvider(startupModuleType, services, options => + { + optionsAction?.Invoke(options); + options.ManualConfigureServices = true; + }); + await app.ConfigureServicesAsync(); + return app; + } + public static IAbpApplicationWithInternalServiceProvider Create( [CanBeNull] Action optionsAction = null) where TStartupModule : IAbpModule diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithExternalServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithExternalServiceProvider.cs index 162d68b5d7..fbb9d611cf 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithExternalServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithExternalServiceProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; @@ -35,6 +36,15 @@ internal class AbpApplicationWithExternalServiceProvider : AbpApplicationBase, I SetServiceProvider(serviceProvider); } + public async Task InitializeAsync(IServiceProvider serviceProvider) + { + Check.NotNull(serviceProvider, nameof(serviceProvider)); + + SetServiceProvider(serviceProvider); + + await InitializeModulesAsync(); + } + public void Initialize([NotNull] IServiceProvider serviceProvider) { Check.NotNull(serviceProvider, nameof(serviceProvider)); diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs index eb118410cd..245ce6fb06 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; @@ -44,6 +45,12 @@ internal class AbpApplicationWithInternalServiceProvider : AbpApplicationBase, I return ServiceProvider; } + public async Task InitializeAsync() + { + CreateServiceProvider(); + await InitializeModulesAsync(); + } + public void Initialize() { CreateServiceProvider(); diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs index de9201bebc..b51fd7ec37 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; @@ -23,6 +24,11 @@ public interface IAbpApplication : IModuleContainer, IDisposable /// IServiceProvider ServiceProvider { get; } + /// + /// Used to gracefully shutdown the application and all modules. + /// + Task ShutdownAsync(); + /// /// Used to gracefully shutdown the application and all modules. /// diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplicationWithExternalServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplicationWithExternalServiceProvider.cs index 91c95f4518..b4363e1161 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplicationWithExternalServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplicationWithExternalServiceProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using JetBrains.Annotations; namespace Volo.Abp; @@ -10,6 +11,18 @@ public interface IAbpApplicationWithExternalServiceProvider : IAbpApplication /// void SetServiceProvider([NotNull] IServiceProvider serviceProvider); + /// + /// Call the Pre/Post/ConfigureServicesAsync method of the modules. + /// + Task ConfigureServicesAsync(); + + /// + /// Sets the service provider and initializes all the modules. + /// If was called before, the same + /// instance should be passed to this method. + /// + Task InitializeAsync([NotNull] IServiceProvider serviceProvider); + /// /// Sets the service provider and initializes all the modules. /// If was called before, the same diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplicationWithInternalServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplicationWithInternalServiceProvider.cs index 61fdb0652f..8a3b4c7ca1 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplicationWithInternalServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplicationWithInternalServiceProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; namespace Volo.Abp; @@ -10,6 +11,18 @@ public interface IAbpApplicationWithInternalServiceProvider : IAbpApplication /// IServiceProvider CreateServiceProvider(); + /// + /// Call the Pre/Post/ConfigureServicesAsync method of the modules. + /// + Task ConfigureServicesAsync(); + + /// + /// Creates the service provider and initializes all the modules. + /// If method was called before, + /// it does not re-create it, but uses the previous one. + /// + Task InitializeAsync(); + /// /// Creates the service provider and initializes all the modules. /// If method was called before, diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IOnApplicationInitialization.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IOnApplicationInitialization.cs index 5ed95a545b..6131a8b710 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IOnApplicationInitialization.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IOnApplicationInitialization.cs @@ -1,8 +1,11 @@ -using JetBrains.Annotations; +using System.Threading.Tasks; +using JetBrains.Annotations; namespace Volo.Abp; public interface IOnApplicationInitialization { + Task OnApplicationInitializationAsync([NotNull] ApplicationInitializationContext context); + void OnApplicationInitialization([NotNull] ApplicationInitializationContext context); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IOnApplicationShutdown.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IOnApplicationShutdown.cs index ced040851e..9f67ebb466 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IOnApplicationShutdown.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IOnApplicationShutdown.cs @@ -1,8 +1,11 @@ -using JetBrains.Annotations; +using System.Threading.Tasks; +using JetBrains.Annotations; namespace Volo.Abp; public interface IOnApplicationShutdown { + Task OnApplicationShutdownAsync([NotNull] ApplicationShutdownContext context); + void OnApplicationShutdown([NotNull] ApplicationShutdownContext context); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/AbpModule.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/AbpModule.cs index 537ccc54f6..ae2bec3112 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/AbpModule.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/AbpModule.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -30,36 +31,78 @@ public abstract class AbpModule : private ServiceConfigurationContext _serviceConfigurationContext; + public virtual Task PreConfigureServicesAsync(ServiceConfigurationContext context) + { + PreConfigureServices(context); + return Task.CompletedTask; + } + public virtual void PreConfigureServices(ServiceConfigurationContext context) { } + public virtual Task ConfigureServicesAsync(ServiceConfigurationContext context) + { + ConfigureServices(context); + return Task.CompletedTask; + } + public virtual void ConfigureServices(ServiceConfigurationContext context) { } + public virtual Task PostConfigureServicesAsync(ServiceConfigurationContext context) + { + PostConfigureServices(context); + return Task.CompletedTask; + } + public virtual void PostConfigureServices(ServiceConfigurationContext context) { } + public virtual Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context) + { + OnPreApplicationInitialization(context); + return Task.CompletedTask; + } + public virtual void OnPreApplicationInitialization(ApplicationInitializationContext context) { } + public virtual Task OnApplicationInitializationAsync(ApplicationInitializationContext context) + { + OnApplicationInitialization(context); + return Task.CompletedTask; + } + public virtual void OnApplicationInitialization(ApplicationInitializationContext context) { } + public virtual Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context) + { + OnPostApplicationInitialization(context); + return Task.CompletedTask; + } + public virtual void OnPostApplicationInitialization(ApplicationInitializationContext context) { } + public virtual Task OnApplicationShutdownAsync(ApplicationShutdownContext context) + { + OnApplicationShutdown(context); + return Task.CompletedTask; + } + public virtual void OnApplicationShutdown(ApplicationShutdownContext context) { diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/DefaultModuleLifecycleContributor.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/DefaultModuleLifecycleContributor.cs index f548cfaafb..c520cc8d58 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/DefaultModuleLifecycleContributor.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/DefaultModuleLifecycleContributor.cs @@ -1,7 +1,17 @@ -namespace Volo.Abp.Modularity; +using System.Threading.Tasks; + +namespace Volo.Abp.Modularity; public class OnApplicationInitializationModuleLifecycleContributor : ModuleLifecycleContributorBase { + public async override Task InitializeAsync(ApplicationInitializationContext context, IAbpModule module) + { + if (module is IOnApplicationInitialization onApplicationInitialization) + { + await onApplicationInitialization.OnApplicationInitializationAsync(context); + } + } + public override void Initialize(ApplicationInitializationContext context, IAbpModule module) { (module as IOnApplicationInitialization)?.OnApplicationInitialization(context); @@ -10,6 +20,14 @@ public class OnApplicationInitializationModuleLifecycleContributor : ModuleLifec public class OnApplicationShutdownModuleLifecycleContributor : ModuleLifecycleContributorBase { + public async override Task ShutdownAsync(ApplicationShutdownContext context, IAbpModule module) + { + if (module is IOnApplicationShutdown onApplicationShutdown) + { + await onApplicationShutdown.OnApplicationShutdownAsync(context); + } + } + public override void Shutdown(ApplicationShutdownContext context, IAbpModule module) { (module as IOnApplicationShutdown)?.OnApplicationShutdown(context); @@ -18,6 +36,14 @@ public class OnApplicationShutdownModuleLifecycleContributor : ModuleLifecycleCo public class OnPreApplicationInitializationModuleLifecycleContributor : ModuleLifecycleContributorBase { + public async override Task InitializeAsync(ApplicationInitializationContext context, IAbpModule module) + { + if (module is IOnPreApplicationInitialization onPreApplicationInitialization) + { + await onPreApplicationInitialization.OnPreApplicationInitializationAsync(context); + } + } + public override void Initialize(ApplicationInitializationContext context, IAbpModule module) { (module as IOnPreApplicationInitialization)?.OnPreApplicationInitialization(context); @@ -26,6 +52,14 @@ public class OnPreApplicationInitializationModuleLifecycleContributor : ModuleLi public class OnPostApplicationInitializationModuleLifecycleContributor : ModuleLifecycleContributorBase { + public async override Task InitializeAsync(ApplicationInitializationContext context, IAbpModule module) + { + if (module is IOnPostApplicationInitialization onPostApplicationInitialization) + { + await onPostApplicationInitialization.OnPostApplicationInitializationAsync(context); + } + } + public override void Initialize(ApplicationInitializationContext context, IAbpModule module) { (module as IOnPostApplicationInitialization)?.OnPostApplicationInitialization(context); diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IAbpModule.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IAbpModule.cs index b992020d4c..3622a50324 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IAbpModule.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IAbpModule.cs @@ -1,6 +1,10 @@ -namespace Volo.Abp.Modularity; +using System.Threading.Tasks; + +namespace Volo.Abp.Modularity; public interface IAbpModule { + Task ConfigureServicesAsync(ServiceConfigurationContext context); + void ConfigureServices(ServiceConfigurationContext context); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IModuleLifecycleContributor.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IModuleLifecycleContributor.cs index 299bfe9e5a..ca0ad1cfd8 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IModuleLifecycleContributor.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IModuleLifecycleContributor.cs @@ -1,11 +1,16 @@ -using JetBrains.Annotations; +using System.Threading.Tasks; +using JetBrains.Annotations; using Volo.Abp.DependencyInjection; namespace Volo.Abp.Modularity; public interface IModuleLifecycleContributor : ITransientDependency { + Task InitializeAsync([NotNull] ApplicationInitializationContext context, [NotNull] IAbpModule module); + void Initialize([NotNull] ApplicationInitializationContext context, [NotNull] IAbpModule module); + Task ShutdownAsync([NotNull] ApplicationShutdownContext context, [NotNull] IAbpModule module); + void Shutdown([NotNull] ApplicationShutdownContext context, [NotNull] IAbpModule module); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IModuleManager.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IModuleManager.cs index d981b745b9..eb4cf52e3c 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IModuleManager.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IModuleManager.cs @@ -1,10 +1,15 @@ -using JetBrains.Annotations; +using System.Threading.Tasks; +using JetBrains.Annotations; namespace Volo.Abp.Modularity; public interface IModuleManager { + Task InitializeModulesAsync([NotNull] ApplicationInitializationContext context); + void InitializeModules([NotNull] ApplicationInitializationContext context); + Task ShutdownModulesAsync([NotNull] ApplicationShutdownContext context); + void ShutdownModules([NotNull] ApplicationShutdownContext context); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IOnPostApplicationInitialization.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IOnPostApplicationInitialization.cs index d79a0318ec..26177360f9 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IOnPostApplicationInitialization.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IOnPostApplicationInitialization.cs @@ -1,8 +1,11 @@ -using JetBrains.Annotations; +using System.Threading.Tasks; +using JetBrains.Annotations; namespace Volo.Abp.Modularity; public interface IOnPostApplicationInitialization { + Task OnPostApplicationInitializationAsync([NotNull] ApplicationInitializationContext context); + void OnPostApplicationInitialization([NotNull] ApplicationInitializationContext context); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IOnPreApplicationInitialization.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IOnPreApplicationInitialization.cs index 0426ef50af..7e63301e3e 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IOnPreApplicationInitialization.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IOnPreApplicationInitialization.cs @@ -1,8 +1,11 @@ -using JetBrains.Annotations; +using System.Threading.Tasks; +using JetBrains.Annotations; namespace Volo.Abp.Modularity; public interface IOnPreApplicationInitialization { + Task OnPreApplicationInitializationAsync([NotNull] ApplicationInitializationContext context); + void OnPreApplicationInitialization([NotNull] ApplicationInitializationContext context); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IPostConfigureServices.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IPostConfigureServices.cs index 38d3449b85..a1ea07de27 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IPostConfigureServices.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IPostConfigureServices.cs @@ -1,8 +1,10 @@ -using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; namespace Volo.Abp.Modularity; public interface IPostConfigureServices { + Task PostConfigureServicesAsync(ServiceConfigurationContext context); + void PostConfigureServices(ServiceConfigurationContext context); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IPreConfigureServices.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IPreConfigureServices.cs index 51db8f5fb8..dfb9eef1df 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IPreConfigureServices.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IPreConfigureServices.cs @@ -1,8 +1,10 @@ -using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; namespace Volo.Abp.Modularity; public interface IPreConfigureServices { + Task PreConfigureServicesAsync(ServiceConfigurationContext context); + void PreConfigureServices(ServiceConfigurationContext context); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLifecycleContributorBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLifecycleContributorBase.cs index b3ac566eab..3192d1f2ea 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLifecycleContributorBase.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLifecycleContributorBase.cs @@ -1,11 +1,23 @@ -namespace Volo.Abp.Modularity; +using System.Threading.Tasks; + +namespace Volo.Abp.Modularity; public abstract class ModuleLifecycleContributorBase : IModuleLifecycleContributor { + public virtual Task InitializeAsync(ApplicationInitializationContext context, IAbpModule module) + { + return Task.CompletedTask; + } + public virtual void Initialize(ApplicationInitializationContext context, IAbpModule module) { } + public virtual Task ShutdownAsync(ApplicationShutdownContext context, IAbpModule module) + { + return Task.CompletedTask; + } + public virtual void Shutdown(ApplicationShutdownContext context, IAbpModule module) { } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs index 2218b2f6fd..71697a1a83 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -30,6 +31,26 @@ public class ModuleManager : IModuleManager, ISingletonDependency .ToArray(); } + public virtual async Task InitializeModulesAsync(ApplicationInitializationContext context) + { + foreach (var contributor in _lifecycleContributors) + { + foreach (var module in _moduleContainer.Modules) + { + try + { + await contributor.InitializeAsync(context, module.Instance); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during the initialize {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}: {ex.Message}. See the inner exception for details.", ex); + } + } + } + + _logger.LogInformation("Initialized all ABP modules."); + } + public void InitializeModules(ApplicationInitializationContext context) { foreach (var contributor in _lifecycleContributors) @@ -50,6 +71,26 @@ public class ModuleManager : IModuleManager, ISingletonDependency _logger.LogInformation("Initialized all ABP modules."); } + public virtual async Task ShutdownModulesAsync(ApplicationShutdownContext context) + { + var modules = _moduleContainer.Modules.Reverse().ToList(); + + foreach (var contributor in _lifecycleContributors) + { + foreach (var module in modules) + { + try + { + await contributor.ShutdownAsync(context, module.Instance); + } + catch (Exception ex) + { + throw new AbpShutdownException($"An error occurred during the shutdown {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}: {ex.Message}. See the inner exception for details.", ex); + } + } + } + } + public void ShutdownModules(ApplicationShutdownContext context) { var modules = _moduleContainer.Modules.Reverse().ToList(); diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs index 63bc7f5f78..f69d94ce61 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Modularity; using Volo.Abp.Modularity.PlugIns; @@ -8,6 +9,39 @@ namespace Volo.Abp; public class AbpApplication_Initialize_Tests { + [Fact] + public async Task Should_Initialize_Single_Module_Async() + { + using (var application = await AbpApplicationFactory.CreateAsync()) + { + //Assert + var module = application.Services.GetSingletonInstance(); + + module.PreConfigureServicesAsyncIsCalled.ShouldBeTrue(); + module.PreConfigureServicesIsCalled.ShouldBeTrue(); + + module.ConfigureServicesAsyncIsCalled.ShouldBeTrue(); + module.ConfigureServicesIsCalled.ShouldBeTrue(); + + module.PostConfigureServicesAsyncIsCalled.ShouldBeTrue(); + module.PostConfigureServicesIsCalled.ShouldBeTrue(); + + //Act + await application.InitializeAsync(); + + //Assert + application.ServiceProvider.GetRequiredService().ShouldBeSameAs(module); + module.OnApplicationInitializeAsyncIsCalled.ShouldBeTrue(); + module.OnApplicationInitializeIsCalled.ShouldBeTrue(); + //Act + await application.ShutdownAsync(); + + //Assert + module.OnApplicationShutdownAsyncIsCalled.ShouldBeTrue(); + module.OnApplicationShutdownIsCalled.ShouldBeTrue(); + } + } + [Fact] public void Should_Initialize_Single_Module() { @@ -34,6 +68,50 @@ public class AbpApplication_Initialize_Tests } } + [Fact] + public async Task Should_Initialize_PlugIn_Async() + { + using (var application = await AbpApplicationFactory.CreateAsync(options => + { + options.PlugInSources.AddTypes(typeof(IndependentEmptyPlugInModule)); + })) + { + //Assert + var plugInModule = application.Services.GetSingletonInstance(); + + plugInModule.PreConfigureServicesAsyncIsCalled.ShouldBeTrue(); + plugInModule.PreConfigureServicesIsCalled.ShouldBeTrue(); + + plugInModule.ConfigureServicesAsyncIsCalled.ShouldBeTrue(); + plugInModule.ConfigureServicesIsCalled.ShouldBeTrue(); + + plugInModule.PostConfigureServicesAsyncIsCalled.ShouldBeTrue(); + plugInModule.PostConfigureServicesIsCalled.ShouldBeTrue(); + + //Act + await application.InitializeAsync(); + + //Assert + application.ServiceProvider.GetRequiredService().ShouldBeSameAs(plugInModule); + + plugInModule.OnPreApplicationInitializationAsyncIsCalled.ShouldBeTrue(); + plugInModule.OnPreApplicationInitializationIsCalled.ShouldBeTrue(); + + plugInModule.OnApplicationInitializeAsyncIsCalled.ShouldBeTrue(); + plugInModule.OnApplicationInitializeIsCalled.ShouldBeTrue(); + + plugInModule.OnPostApplicationInitializationAsyncIsCalled.ShouldBeTrue(); + plugInModule.OnPostApplicationInitializationIsCalled.ShouldBeTrue(); + + //Act + await application.ShutdownAsync(); + + //Assert + plugInModule.OnApplicationShutdownAsyncIsCalled.ShouldBeTrue(); + plugInModule.OnApplicationShutdownIsCalled.ShouldBeTrue(); + } + } + [Fact] public void Should_Initialize_PlugIn() { @@ -53,7 +131,9 @@ public class AbpApplication_Initialize_Tests //Assert application.ServiceProvider.GetRequiredService().ShouldBeSameAs(plugInModule); + plugInModule.OnPreApplicationInitializationIsCalled.ShouldBeTrue(); plugInModule.OnApplicationInitializeIsCalled.ShouldBeTrue(); + plugInModule.OnPostApplicationInitializationIsCalled.ShouldBeTrue(); //Act application.Shutdown(); diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Configuration/ConfigurationAccessor_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Configuration/ConfigurationAccessor_Tests.cs index 9023fb6ce7..4a0dda26fb 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Configuration/ConfigurationAccessor_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Configuration/ConfigurationAccessor_Tests.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Configuration; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Modularity; @@ -8,6 +9,22 @@ namespace Volo.Abp.Configuration; public class ConfigurationAccessor_Tests { + [Fact] + public async Task Should_Use_Empty_ConfigurationRoot_By_Default_Async() + { + using (var application = await AbpApplicationFactory.CreateAsync()) + { + var configuration1 = application.Services.GetConfiguration(); + configuration1.ShouldNotBeNull(); + + await application.InitializeAsync(); + + var configuration2 = ResolveConfiguration(application); + + configuration2.ShouldBe(configuration1); + } + } + [Fact] public void Should_Use_Empty_ConfigurationRoot_By_Default() { @@ -24,6 +41,23 @@ public class ConfigurationAccessor_Tests } } + [Fact] + public async Task Should_Use_The_Registered_ConfigurationRoot_Async() + { + using (var application = await AbpApplicationFactory.CreateAsync()) + { + var myConfiguration = new ConfigurationBuilder().Build(); + application.Services.ReplaceConfiguration(myConfiguration); + application.Services.GetConfiguration().ShouldBe(myConfiguration); + + await application.InitializeAsync(); + + var configuration = ResolveConfiguration(application); + + configuration.ShouldBe(myConfiguration); + } + } + [Fact] public void Should_Use_The_Registered_ConfigurationRoot() { diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/AutoServiceRegistration_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/AutoServiceRegistration_Tests.cs index 6fc5716a22..71ec559824 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/AutoServiceRegistration_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/AutoServiceRegistration_Tests.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Modularity; @@ -8,6 +9,20 @@ namespace Volo.Abp.DependencyInjection; public class AutoServiceRegistration_Tests { + [Fact] + public async Task AutoServiceRegistration_Should_Not_Duplicate_Test_Async() + { + using (var application = await AbpApplicationFactory.CreateAsync()) + { + //Act + await application.InitializeAsync(); + + //Assert + var services = application.ServiceProvider.GetServices().ToList(); + services.Count.ShouldBe(1); + } + } + [Fact] public void AutoServiceRegistration_Should_Not_Duplicate_Test() { diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/HybridServiceScopeFactory_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/HybridServiceScopeFactory_Tests.cs index d665c863dc..6a3f059ba8 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/HybridServiceScopeFactory_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/HybridServiceScopeFactory_Tests.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Modularity; @@ -8,6 +9,26 @@ namespace Volo.Abp.DependencyInjection; public class HybridServiceScopeFactory_Tests { + [Fact] + public async Task Should_Use_Default_ServiceScopeFactory_By_Default_Async() + { + using (var application = await AbpApplicationFactory.CreateAsync()) + { + application.Services.AddType(typeof(MyServiceAsync)); + + await application.InitializeAsync(); + + var serviceScopeFactory = application.ServiceProvider.GetRequiredService(); + + using (var scope = serviceScopeFactory.CreateScope()) + { + scope.ServiceProvider.GetRequiredService(); + } + + MyServiceAsync.DisposeCount.ShouldBe(1); + } + } + [Fact] public void Should_Use_Default_ServiceScopeFactory_By_Default() { @@ -28,6 +49,16 @@ public class HybridServiceScopeFactory_Tests } } + private class MyServiceAsync : ITransientDependency, IDisposable + { + public static int DisposeCount { get; private set; } + + public void Dispose() + { + ++DisposeCount; + } + } + private class MyService : ITransientDependency, IDisposable { public static int DisposeCount { get; private set; } diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Modularity/TestModuleBase.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Modularity/TestModuleBase.cs index 327d2982a5..29b7927c64 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Modularity/TestModuleBase.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Modularity/TestModuleBase.cs @@ -1,39 +1,97 @@ -using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; namespace Volo.Abp.Modularity; public class TestModuleBase : AbpModule { - public bool PreConfigureServicesIsCalled { get; set; } + public bool PreConfigureServicesAsyncIsCalled { get; set; } + public bool ConfigureServicesAsyncIsCalled { get; set; } + public bool PostConfigureServicesAsyncIsCalled { get; set; } + public bool OnPreApplicationInitializationIsCalled { get; set; } + public bool OnPostApplicationInitializationIsCalled { get; set; } + public bool OnApplicationInitializeAsyncIsCalled { get; set; } + public bool OnApplicationShutdownAsyncIsCalled { get; set; } + public bool PreConfigureServicesIsCalled { get; set; } public bool ConfigureServicesIsCalled { get; set; } - public bool PostConfigureServicesIsCalled { get; set; } - + public bool OnPreApplicationInitializationAsyncIsCalled { get; set; } + public bool OnPostApplicationInitializationAsyncIsCalled { get; set; } public bool OnApplicationInitializeIsCalled { get; set; } - public bool OnApplicationShutdownIsCalled { get; set; } + public override Task PreConfigureServicesAsync(ServiceConfigurationContext context) + { + PreConfigureServicesAsyncIsCalled = true; + return base.PreConfigureServicesAsync(context); + } + public override void PreConfigureServices(ServiceConfigurationContext context) { PreConfigureServicesIsCalled = true; } + public override Task ConfigureServicesAsync(ServiceConfigurationContext context) + { + ConfigureServicesAsyncIsCalled = true; + return base.ConfigureServicesAsync(context); + } + public override void ConfigureServices(ServiceConfigurationContext context) { ConfigureServicesIsCalled = true; } + public override Task PostConfigureServicesAsync(ServiceConfigurationContext context) + { + PostConfigureServicesAsyncIsCalled = true; + return base.PostConfigureServicesAsync(context); + } + public override void PostConfigureServices(ServiceConfigurationContext context) { PostConfigureServicesIsCalled = true; } + public override Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context) + { + OnPreApplicationInitializationAsyncIsCalled = true; + return base.OnPreApplicationInitializationAsync(context); + } + + public override void OnPreApplicationInitialization(ApplicationInitializationContext context) + { + OnPreApplicationInitializationIsCalled = true; + } + + public override Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context) + { + OnPostApplicationInitializationAsyncIsCalled = true; + return base.OnPostApplicationInitializationAsync(context); + } + + public override void OnPostApplicationInitialization(ApplicationInitializationContext context) + { + OnPostApplicationInitializationIsCalled = true; + } + + public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) + { + OnApplicationInitializeAsyncIsCalled = true; + return base.OnApplicationInitializationAsync(context); + } + public override void OnApplicationInitialization(ApplicationInitializationContext context) { OnApplicationInitializeIsCalled = true; } + public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context) + { + OnApplicationShutdownAsyncIsCalled = true; + return base.OnApplicationShutdownAsync(context); + } + public override void OnApplicationShutdown(ApplicationShutdownContext context) { OnApplicationShutdownIsCalled = true;