diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index 4f4919082b..a6f132ea1e 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -49,9 +49,9 @@ namespace Volo.Abp.AspNetCore.Mvc return; } - var moduleManager = context.ServiceProvider.GetRequiredService(); + var moduleContainer = context.ServiceProvider.GetRequiredService(); - foreach (var module in moduleManager.Modules.Where(m => m.IsLoadedAsPlugIn)) + foreach (var module in moduleContainer.Modules.Where(m => m.IsLoadedAsPlugIn)) { partManager.ApplicationParts.Add(new AssemblyPart(module.Type.GetTypeInfo().Assembly)); } diff --git a/src/Volo.Abp/Volo/Abp/AbpApplicationBase.cs b/src/Volo.Abp/Volo/Abp/AbpApplicationBase.cs index ad4941451f..fa0158911b 100644 --- a/src/Volo.Abp/Volo/Abp/AbpApplicationBase.cs +++ b/src/Volo.Abp/Volo/Abp/AbpApplicationBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Internal; @@ -15,8 +16,7 @@ namespace Volo.Abp public IServiceCollection Services { get; } - [NotNull] - public AbpModuleDescriptor[] Modules { get; } + public IReadOnlyList Modules { get; } internal AbpApplicationBase( [NotNull] Type startupModuleType, @@ -33,12 +33,13 @@ namespace Volo.Abp optionsAction?.Invoke(options); services.AddSingleton(_ => this); + services.AddSingleton(_ => this); services.AddCoreAbpServices(); Modules = LoadModules(services, options); } - private AbpModuleDescriptor[] LoadModules(IServiceCollection services, AbpApplicationCreationOptions options) + private IReadOnlyList LoadModules(IServiceCollection services, AbpApplicationCreationOptions options) { return services .GetSingletonInstance() diff --git a/src/Volo.Abp/Volo/Abp/IAbpApplication.cs b/src/Volo.Abp/Volo/Abp/IAbpApplication.cs index a5b961a409..4dcb7f4832 100644 --- a/src/Volo.Abp/Volo/Abp/IAbpApplication.cs +++ b/src/Volo.Abp/Volo/Abp/IAbpApplication.cs @@ -4,7 +4,7 @@ using Volo.Abp.Modularity; namespace Volo.Abp { - public interface IAbpApplication : IDisposable + public interface IAbpApplication : IModuleContainer, IDisposable { /// /// Type of the startup (entrance) module of the application. @@ -23,11 +23,6 @@ namespace Volo.Abp /// IServiceProvider ServiceProvider { get; } - /// - /// List of modules loaded into this application. - /// - AbpModuleDescriptor[] Modules { get; } - /// /// Used to gracefully shutdown the application and all modules. /// diff --git a/src/Volo.Abp/Volo/Abp/Modularity/AbpModuleDescriptor.cs b/src/Volo.Abp/Volo/Abp/Modularity/AbpModuleDescriptor.cs index f7ab1777d1..87ce07cffa 100644 --- a/src/Volo.Abp/Volo/Abp/Modularity/AbpModuleDescriptor.cs +++ b/src/Volo.Abp/Volo/Abp/Modularity/AbpModuleDescriptor.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Reflection; using JetBrains.Annotations; +using Volo.ExtensionMethods.Collections.Generic; namespace Volo.Abp.Modularity { - public class AbpModuleDescriptor + public class AbpModuleDescriptor : IAbpModuleDescriptor { public Type Type { get; } @@ -13,9 +15,13 @@ namespace Volo.Abp.Modularity public bool IsLoadedAsPlugIn { get; } - internal List Dependencies { get; } + public IReadOnlyList Dependencies => _dependencies.ToImmutableList(); + private readonly List _dependencies; - public AbpModuleDescriptor([NotNull] Type type, [NotNull] IAbpModule instance, bool isLoadedAsPlugIn) + public AbpModuleDescriptor( + [NotNull] Type type, + [NotNull] IAbpModule instance, + bool isLoadedAsPlugIn) { Check.NotNull(type, nameof(type)); Check.NotNull(instance, nameof(instance)); @@ -29,7 +35,12 @@ namespace Volo.Abp.Modularity Instance = instance; IsLoadedAsPlugIn = isLoadedAsPlugIn; - Dependencies = new List(); + _dependencies = new List(); + } + + public void AddDependency(IAbpModuleDescriptor descriptor) + { + _dependencies.AddIfNotContains(descriptor); } public override string ToString() diff --git a/src/Volo.Abp/Volo/Abp/Modularity/IAbpModuleDescriptor.cs b/src/Volo.Abp/Volo/Abp/Modularity/IAbpModuleDescriptor.cs new file mode 100644 index 0000000000..542010ee04 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Modularity/IAbpModuleDescriptor.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Modularity +{ + public interface IAbpModuleDescriptor + { + Type Type { get; } + + IAbpModule Instance { get; } + + bool IsLoadedAsPlugIn { get; } + + IReadOnlyList Dependencies { get; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Modularity/IModuleContainer.cs b/src/Volo.Abp/Volo/Abp/Modularity/IModuleContainer.cs new file mode 100644 index 0000000000..ebd0927536 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Modularity/IModuleContainer.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.Modularity +{ + public interface IModuleContainer + { + [NotNull] + IReadOnlyList Modules { get; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Modularity/IModuleLoader.cs b/src/Volo.Abp/Volo/Abp/Modularity/IModuleLoader.cs index 7e31ffa6df..6582d489b0 100644 --- a/src/Volo.Abp/Volo/Abp/Modularity/IModuleLoader.cs +++ b/src/Volo.Abp/Volo/Abp/Modularity/IModuleLoader.cs @@ -8,6 +8,10 @@ namespace Volo.Abp.Modularity public interface IModuleLoader { [NotNull] - AbpModuleDescriptor[] LoadModules([NotNull] IServiceCollection services, [NotNull] Type startupModuleType, [NotNull] PlugInSourceList plugInSources); + IAbpModuleDescriptor[] LoadModules( + [NotNull] IServiceCollection services, + [NotNull] Type startupModuleType, + [NotNull] PlugInSourceList plugInSources + ); } } diff --git a/src/Volo.Abp/Volo/Abp/Modularity/IModuleManager.cs b/src/Volo.Abp/Volo/Abp/Modularity/IModuleManager.cs index 06e4a8c790..cb68fa770f 100644 --- a/src/Volo.Abp/Volo/Abp/Modularity/IModuleManager.cs +++ b/src/Volo.Abp/Volo/Abp/Modularity/IModuleManager.cs @@ -1,13 +1,9 @@ -using System.Collections.Generic; -using JetBrains.Annotations; +using JetBrains.Annotations; namespace Volo.Abp.Modularity { public interface IModuleManager { - [NotNull] - IReadOnlyList Modules { get; } - void InitializeModules([NotNull] ApplicationInitializationContext context); void ShutdownModules([NotNull] ApplicationShutdownContext context); diff --git a/src/Volo.Abp/Volo/Abp/Modularity/ModuleLoader.cs b/src/Volo.Abp/Volo/Abp/Modularity/ModuleLoader.cs index 9d56703f77..00f8c6e093 100644 --- a/src/Volo.Abp/Volo/Abp/Modularity/ModuleLoader.cs +++ b/src/Volo.Abp/Volo/Abp/Modularity/ModuleLoader.cs @@ -9,7 +9,7 @@ namespace Volo.Abp.Modularity { public class ModuleLoader : IModuleLoader { - public AbpModuleDescriptor[] LoadModules( + public IAbpModuleDescriptor[] LoadModules( IServiceCollection services, Type startupModuleType, PlugInSourceList plugInSources) @@ -18,18 +18,27 @@ namespace Volo.Abp.Modularity Check.NotNull(startupModuleType, nameof(startupModuleType)); Check.NotNull(plugInSources, nameof(plugInSources)); - var modules = new List(); - - FillModules(modules, services, startupModuleType, plugInSources); - SetModuleDependencies(modules); + var modules = GetDescriptors(services, startupModuleType, plugInSources); modules = SortByDependency(modules, startupModuleType); - ConfigureServices(modules, services); return modules.ToArray(); } + private List GetDescriptors( + IServiceCollection services, + Type startupModuleType, + PlugInSourceList plugInSources) + { + var modules = new List(); + + FillModules(modules, services, startupModuleType, plugInSources); + SetDependencies(modules); + + return modules.Cast().ToList(); + } + protected virtual void FillModules( List modules, IServiceCollection services, @@ -54,15 +63,15 @@ namespace Volo.Abp.Modularity } } - protected virtual void SetModuleDependencies(List modules) + protected virtual void SetDependencies(List modules) { foreach (var module in modules) { - SetModuleDependencies(modules, module); + SetDependencies(modules, module); } } - protected virtual List SortByDependency(List modules, Type startupModuleType) + protected virtual List SortByDependency(List modules, Type startupModuleType) { var sortedModules = modules.SortByDependencies(m => m.Dependencies); sortedModules.MoveItem(m => m.Type == typeof(AbpKernelModule), 0); @@ -82,7 +91,7 @@ namespace Volo.Abp.Modularity return module; } - protected virtual void ConfigureServices(List modules, IServiceCollection services) + protected virtual void ConfigureServices(List modules, IServiceCollection services) { //PreConfigureServices foreach (var module in modules.Where(m => m.Instance is IPreConfigureServices)) @@ -103,7 +112,7 @@ namespace Volo.Abp.Modularity } } - protected virtual void SetModuleDependencies(List modules, AbpModuleDescriptor module) + protected virtual void SetDependencies(List modules, AbpModuleDescriptor module) { foreach (var dependedModuleType in AbpModuleHelper.FindDependedModuleTypes(module.Type)) { @@ -113,7 +122,7 @@ namespace Volo.Abp.Modularity throw new AbpException("Could not find a depended module " + dependedModuleType.AssemblyQualifiedName + " for " + module.Type.AssemblyQualifiedName); } - module.Dependencies.AddIfNotContains(dependedModule); + module.AddDependency(dependedModule); } } } diff --git a/src/Volo.Abp/Volo/Abp/Modularity/ModuleManager.cs b/src/Volo.Abp/Volo/Abp/Modularity/ModuleManager.cs index 0541b5e6c7..35263373c4 100644 --- a/src/Volo.Abp/Volo/Abp/Modularity/ModuleManager.cs +++ b/src/Volo.Abp/Volo/Abp/Modularity/ModuleManager.cs @@ -1,6 +1,4 @@ using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using Microsoft.Extensions.Logging; using Volo.DependencyInjection; @@ -8,20 +6,18 @@ namespace Volo.Abp.Modularity { public class ModuleManager : IModuleManager, ISingletonDependency { - public IReadOnlyList Modules => _modules.ToImmutableList(); - - private readonly List _modules; + private readonly IModuleContainer _moduleContainer; private readonly IEnumerable _lifecycleContributers; private readonly ILogger _logger; public ModuleManager( - IAbpApplication application, + IModuleContainer moduleContainer, IEnumerable lifecycleContributers, ILogger logger) { + _moduleContainer = moduleContainer; _lifecycleContributers = lifecycleContributers; _logger = logger; - _modules = application.Modules.ToList(); } public void InitializeModules(ApplicationInitializationContext context) @@ -30,7 +26,7 @@ namespace Volo.Abp.Modularity foreach (var contributer in _lifecycleContributers) { - foreach (var module in Modules) + foreach (var module in _moduleContainer.Modules) { contributer.Initialize(context, module.Instance); } @@ -43,7 +39,7 @@ namespace Volo.Abp.Modularity { _logger.LogInformation("Loaded modules:"); - foreach (var module in Modules) + foreach (var module in _moduleContainer.Modules) { _logger.LogInformation("- " + module.Type.FullName); } @@ -53,7 +49,7 @@ namespace Volo.Abp.Modularity { foreach (var contributer in _lifecycleContributers) { - foreach (var module in Modules) + foreach (var module in _moduleContainer.Modules) { contributer.Shutdown(context, module.Instance); } diff --git a/src/Volo.Abp/Volo/Abp/Reflection/AssemblyFinder.cs b/src/Volo.Abp/Volo/Abp/Reflection/AssemblyFinder.cs new file mode 100644 index 0000000000..44a8522c34 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Reflection/AssemblyFinder.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Reflection; +using System.Threading; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Reflection +{ + public class AssemblyFinder : IAssemblyFinder + { + private readonly IModuleContainer _moduleContainer; + + private readonly Lazy> _assemblies; + + public AssemblyFinder(IModuleContainer moduleContainer) + { + _moduleContainer = moduleContainer; + + _assemblies = new Lazy>(FindAll, LazyThreadSafetyMode.ExecutionAndPublication); + } + + public IReadOnlyList Assemblies => _assemblies.Value; + + public IReadOnlyList FindAll() + { + var assemblies = new List(); + + foreach (var module in _moduleContainer.Modules) + { + assemblies.Add(module.Type.GetAssembly()); + } + + return assemblies.Distinct().ToImmutableList(); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Reflection/IAssemblyFinder.cs b/src/Volo.Abp/Volo/Abp/Reflection/IAssemblyFinder.cs new file mode 100644 index 0000000000..2805ed762e --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Reflection/IAssemblyFinder.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Reflection; + +namespace Volo.Abp.Reflection +{ + /// + /// Used to get assemblies in the application. + /// It may not return all assemblies, but those are related with modules. + /// + public interface IAssemblyFinder + { + IReadOnlyList Assemblies { get; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Reflection/ITypeFinder.cs b/src/Volo.Abp/Volo/Abp/Reflection/ITypeFinder.cs new file mode 100644 index 0000000000..f314532592 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Reflection/ITypeFinder.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Reflection +{ + /// + /// Used to get types in the application. + /// It may not return all types, but those are related with modules. + /// + public interface ITypeFinder + { + IReadOnlyList Types { get; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Reflection/TypeFinder.cs b/src/Volo.Abp/Volo/Abp/Reflection/TypeFinder.cs new file mode 100644 index 0000000000..aaa59f0293 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Reflection/TypeFinder.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using Microsoft.Extensions.Logging; +using Volo.ExtensionMethods.Collections.Generic; + +namespace Volo.Abp.Reflection +{ + //TODO: Write unit tests! + + public class TypeFinder : ITypeFinder + { + private readonly IAssemblyFinder _assemblyFinder; + private readonly ILogger _logger; + + private readonly Lazy> _types; + + public TypeFinder(IAssemblyFinder assemblyFinder, ILogger logger) + { + _assemblyFinder = assemblyFinder; + _logger = logger; + + _types = new Lazy>(FindAll, LazyThreadSafetyMode.ExecutionAndPublication); + } + + public IReadOnlyList Types => _types.Value; + + private IReadOnlyList FindAll() + { + var allTypes = new List(); + + foreach (var assembly in _assemblyFinder.Assemblies) + { + try + { + Type[] typesInThisAssembly; + + try + { + typesInThisAssembly = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + typesInThisAssembly = ex.Types; + } + + if (typesInThisAssembly.IsNullOrEmpty()) + { + continue; + } + + allTypes.AddRange(typesInThisAssembly.Where(type => type != null)); + } + catch (Exception ex) + { + _logger.LogWarning(ex.ToString()); + } + } + + return allTypes; + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/SimpleAsyncInterceptor.cs b/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/SimpleAsyncInterceptor.cs index 8153b6fd65..10a072c3c7 100644 --- a/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/SimpleAsyncInterceptor.cs +++ b/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/SimpleAsyncInterceptor.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using Volo.Abp.TestBase.Logging; -using Volo.DependencyInjection; namespace Volo.Abp.DynamicProxy { diff --git a/test/Volo.Abp.Tests/Volo/Abp/Reflection/AssemblyFinder_Tests.cs b/test/Volo.Abp.Tests/Volo/Abp/Reflection/AssemblyFinder_Tests.cs new file mode 100644 index 0000000000..269e3a1cae --- /dev/null +++ b/test/Volo.Abp.Tests/Volo/Abp/Reflection/AssemblyFinder_Tests.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NSubstitute; +using Shouldly; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Abp.Reflection +{ + public class AssemblyFinder_Tests + { + [Theory] + [InlineData(new object[] { new Type[] { } })] + [InlineData(new object[] { new[] { typeof(IndependentEmptyModule) } })] + [InlineData(new object[] { new[] { typeof(AbpKernelModule), typeof(IndependentEmptyModule) } })] + public void Should_Get_Assemblies_Of_All_Modules(Type[] moduleTypes) + { + //Arrange + + var fakeModuleContainer = CreateFakeModuleContainer(moduleTypes); + + //Act + + var assemblyFinder = new AssemblyFinder(fakeModuleContainer); + + //Assert + + assemblyFinder.Assemblies.Count.ShouldBe(moduleTypes.Length); + + foreach (var moduleType in moduleTypes) + { + assemblyFinder.Assemblies.ShouldContain(moduleType.GetAssembly()); + } + } + + private static IModuleContainer CreateFakeModuleContainer(IEnumerable moduleTypes) + { + var moduleDescriptors = moduleTypes.Select(CreateModuleDescriptor).ToList(); + return CreateFakeModuleContainer(moduleDescriptors); + } + + private static IModuleContainer CreateFakeModuleContainer(List moduleDescriptors) + { + var fakeModuleContainer = Substitute.For(); + fakeModuleContainer.Modules.Returns(moduleDescriptors); + return fakeModuleContainer; + } + + private static IAbpModuleDescriptor CreateModuleDescriptor(Type moduleType) + { + var moduleDescriptor = Substitute.For(); + moduleDescriptor.Type.Returns(moduleType); + return moduleDescriptor; + } + } +} diff --git a/test/Volo.Abp.Tests/Volo/Abp/Uow/UnitOfWork_Events_Tests.cs b/test/Volo.Abp.Tests/Volo/Abp/Uow/UnitOfWork_Events_Tests.cs index 6df7d6d403..04c061d284 100644 --- a/test/Volo.Abp.Tests/Volo/Abp/Uow/UnitOfWork_Events_Tests.cs +++ b/test/Volo.Abp.Tests/Volo/Abp/Uow/UnitOfWork_Events_Tests.cs @@ -66,7 +66,6 @@ namespace Volo.Abp.Uow disposed.ShouldBeTrue(); } - [Fact] public void Should_Not_Trigger_Complete_If_Uow_Is_Not_Completed() {