diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index a923fee3f1..263178e0ed 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Options; using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Net; using System.Reflection; @@ -179,6 +180,14 @@ namespace Volo.Abp.AspNetCore.Mvc }); } + public override void PostConfigureServices(ServiceConfigurationContext context) + { + ApplicationPartSorter.Sort( + context.Services.GetSingletonInstance(), + context.Services.GetSingletonInstance() + ); + } + public override void OnApplicationInitialization(ApplicationInitializationContext context) { AddApplicationParts(context); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs new file mode 100644 index 0000000000..76dc950d11 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AspNetCore.Mvc +{ + /// + /// This class is used to align order of the MVC Application Parts with the order of + /// ABP module dependencies. + /// + public static class ApplicationPartSorter + { + public static void Sort(ApplicationPartManager partManager, IModuleContainer moduleContainer) + { + /* Performing a double Reverse() to preserve the original order for non-sorted parts + */ + + var dependencyDictionary = CreateDependencyDictionary(partManager, moduleContainer); + + var sortedParts = partManager + .ApplicationParts + .Reverse() //First Revers + .SortByDependencies(p => dependencyDictionary[p]); + + sortedParts.Reverse(); //Reverse again + + //Replace the original parts with the sorted parts + partManager.ApplicationParts.Clear(); + foreach (var applicationPart in sortedParts) + { + partManager.ApplicationParts.Add(applicationPart); + } + } + + private static Dictionary> CreateDependencyDictionary( + ApplicationPartManager partManager, IModuleContainer moduleContainer) + { + var dependencyDictionary = new Dictionary>(); + + foreach (var applicationPart in partManager.ApplicationParts) + { + dependencyDictionary[applicationPart] = + CreateDependencyList(applicationPart, partManager, moduleContainer); + } + + return dependencyDictionary; + } + + private static List CreateDependencyList( + ApplicationPart applicationPart, + ApplicationPartManager partManager, + IModuleContainer moduleContainer) + { + var list = new List(); + + if (applicationPart is AssemblyPart assemblyPart) + { + AddDependencies(list, assemblyPart, partManager, moduleContainer); + } + else if (applicationPart is CompiledRazorAssemblyPart compiledRazorAssemblyPart) + { + AddDependencies(list, compiledRazorAssemblyPart, partManager, moduleContainer); + } + + return list; + } + + private static void AddDependencies( + List list, + AssemblyPart assemblyPart, + ApplicationPartManager partManager, + IModuleContainer moduleContainer) + { + var dependedAssemblyParts = GetDependedAssemblyParts( + partManager, + moduleContainer, + assemblyPart + ); + + list.AddRange(dependedAssemblyParts); + + foreach (var dependedAssemblyPart in dependedAssemblyParts) + { + var viewsPart = GetViewsPartOrNull(partManager, dependedAssemblyPart); + if (viewsPart != null) + { + list.Add(viewsPart); + } + } + } + + private static void AddDependencies( + List list, + CompiledRazorAssemblyPart compiledRazorAssemblyPart, + ApplicationPartManager partManager, + IModuleContainer moduleContainer) + { + if (!compiledRazorAssemblyPart.Name.EndsWith(".Views")) + { + return; + } + + var originalAssemblyPart = GetOriginalAssemblyPartOrNull(compiledRazorAssemblyPart, partManager); + if (originalAssemblyPart == null) + { + return; + } + + list.Add(originalAssemblyPart); + } + + private static AssemblyPart[] GetDependedAssemblyParts( + ApplicationPartManager partManager, + IModuleContainer moduleContainer, + AssemblyPart assemblyPart) + { + var moduleDescriptor = GetModuleDescriptorForAssemblyOrNull(moduleContainer, assemblyPart.Assembly); + if (moduleDescriptor == null) + { + return Array.Empty(); + } + + var moduleDependedAssemblies = moduleDescriptor.Dependencies.Select(d => d.Assembly).ToArray(); + return partManager.ApplicationParts + .OfType() + .Where(a => a.Assembly.IsIn(moduleDependedAssemblies)) + .Distinct() + .ToArray(); + } + + private static CompiledRazorAssemblyPart GetViewsPartOrNull(ApplicationPartManager partManager, + ApplicationPart assemblyPart) + { + return partManager + .ApplicationParts + .OfType() + .FirstOrDefault(p => p.Name == assemblyPart.Name + ".Views"); + } + + private static AssemblyPart GetOriginalAssemblyPartOrNull( + CompiledRazorAssemblyPart compiledRazorAssemblyPart, + ApplicationPartManager partManager) + { + var originalAssemblyName = compiledRazorAssemblyPart.Name.RemovePostFix(".Views"); + return partManager.ApplicationParts + .OfType() + .FirstOrDefault(p => p.Assembly.GetName().Name == originalAssemblyName); + } + + private static IAbpModuleDescriptor GetModuleDescriptorForAssemblyOrNull(IModuleContainer moduleContainer, + Assembly assembly) + { + return moduleContainer.Modules.FirstOrDefault(m => m.Assembly == assembly); + } + } +} \ No newline at end of file