Source
GitHub:https://github.com/iamoldli/NetModular
Demo Address
Address: http://129.211.40.240:6220
account: admin
password: admin
Front-end framework Demo (provisional)
Address: http://progqx5cu.bkt.clouddn.com/skins/index.html#/
account: admin
password: admin
table of Contents
1, opening
2, to quickly create a service module
3, a data access module introduces
4, modular realization of ideas
Obtain official source
For easy viewing source code, let's get the next official source
Download AspNetCore
Source
git clone --recursive https://github.com/aspnet/AspNetCore
Download Extensions
Source
git clone https://github.com/aspnet/Extensions.git
Loading mechanism ASP.NET Core Controller
Reference Document: application member in ASP.NET Core
In ASP.NET Core
the application by means ApplicationPart
to discover the controller, view of the assembly, or the like numerals MVC helper functions, the application member is made ApplicationPartManager
to the management class. When you call AddMvc
or AddMvcCore
when the method to add MVC-related functions, ASP.NET Core
internal creates ApplicationPartManager
the instance, and then the entrance to the assembly as a starting point to find all the assembly unofficial package of its dependency tree, and added to its ApplicationParts
property, and finally ApplicationPartManager
the examples of injection into the container in a single embodiment mode. Here is the relevant source code:
Source Path: AspNetCore \ src \ Mvc.Core \ src \ DependencyInjection \ MvcCoreServiceCollectionExtensions.cs
public static IMvcCoreBuilder AddMvcCore(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
var partManager = GetApplicationPartManager(services);
//单例模式注入ApplicationPartManager
services.TryAddSingleton(partManager);
ConfigureDefaultFeatureProviders(partManager);
ConfigureDefaultServices(services);
AddMvcCoreServices(services);
var builder = new MvcCoreBuilder(services, partManager);
return builder;
}
//获取ApplicationPartManager实例
private static ApplicationPartManager GetApplicationPartManager(IServiceCollection services)
{
var manager = GetServiceFromCollection<ApplicationPartManager>(services);
if (manager == null)
{
manager = new ApplicationPartManager();
var environment = GetServiceFromCollection<IHostingEnvironment>(services);
var entryAssemblyName = environment?.ApplicationName;
if (string.IsNullOrEmpty(entryAssemblyName))
{
return manager;
}
manager.PopulateDefaultParts(entryAssemblyName);
}
return manager;
}
Source Path: AspNetCore \ src \ Mvc.Core \ src \ ApplicationParts \ ApplicationPartManager.cs
internal void PopulateDefaultParts(string entryAssemblyName)
{
var entryAssembly = Assembly.Load(new AssemblyName(entryAssemblyName));
var assembliesProvider = new ApplicationAssembliesProvider();
//加载入口程序集的依赖项树中的所有非官方包的依赖程序集
var applicationAssemblies = assembliesProvider.ResolveAssemblies(entryAssembly);
foreach (var assembly in applicationAssemblies)
{
var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
foreach (var part in partFactory.GetApplicationParts(assembly))
{
ApplicationParts.Add(part);
}
}
}
Because all of our modules are installed by nuget package, it will be automatically compiled into the dependency tree, that is, we do not need to manually load the assembly module.
For the assembly at compile time is not referenced, we can manually load the member by application
// create an assembly part from a class's assembly
var assembly = typeof(Startup).GetTypeInfo().Assembly;
services.AddMvc()
.AddApplicationPart(assembly);
// OR
var assembly = typeof(Startup).GetTypeInfo().Assembly;
var part = new AssemblyPart(assembly);
services.AddMvc()
.ConfigureApplicationPartManager(apm => apm.ApplicationParts.Add(part));
Loading mechanism module
NetModular
The rule is that when a project starts, look under the root directory of the program modules
directory, the directory information is saved exclusively for all modules, and its structure is as follows:
modules
Each represents a subdirectory of the module, which has a subdirectory for each module.json
file that is used to describe the module information, which structure is as follows:
{"Id": "Admin","Name":"权限管理","Version":"1.0.0"}
- Note:
module.json
the file is automatically generated and packaged into a bag while Nuget module compiled automatically when the installation module included in the project. This uses MSBuild , we are interested can look at. *
The following is to generate module.json
the configuration information corresponding to the file
<Project>
<PropertyGroup>
<ModulesDir>modules\$(Id)</ModulesDir>
<ModuleInfo>{"Id": "$(Id)","Name":"$(Name)","Version":"$(Version)"}</ModuleInfo>
</PropertyGroup>
<ItemGroup>
<Content Include="$(ModulesDir)\**">
<Pack>true</Pack>
<PackagePath>contentFiles\any\any\$(ModulesDir)</PackagePath>
<PackageCopyToOutput>true</PackageCopyToOutput>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>Modules\$(Id)\%(RecursiveDir)%(FileName)%(Extension)</TargetPath>
</Content>
</ItemGroup>
<Target Name="ModulesBuildBefore" AfterTargets="Build">
<!--创建modules目录-->
<MakeDir Directories="$(ModulesDir)"/>
<!--生成module.json文件,Note:项目需要生成两次,否则Nuget包中的文件不是最新的-->
<WriteLinesToFile File="$(ModulesDir)\module.json" Overwrite="true" Lines="$(ModuleInfo)" />
</Target>
</Project>
NetModular
It defines a module information described ModuleInfo.cs
class information and a storage module IModuleCollection.cs
interfaces
/// <summary>
/// 模块信息
/// </summary>
public class ModuleInfo
{
/// <summary>
/// 编号
/// </summary>
public string Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 版本
/// </summary>
public string Version { get; set; }
/// <summary>
/// 模块初始化器
/// </summary>
public IModuleInitializer Initializer { get; set; }
/// <summary>
/// 程序集信息
/// </summary>
public ModuleAssembliesInfo AssembliesInfo { get; set; }
}
/// <summary>
/// 模块集合
/// </summary>
public interface IModuleCollection : IList<ModuleInfo>
{
}
IModuleCollection
Have implemented a class ModuleCollection.cs
, is loaded module list operation performed in the constructor of the class:
public ModuleCollection()
{
var moduleJsonFiles = Directory.GetFiles(Path.Combine(AppContext.BaseDirectory, "modules"), "module.json", SearchOption.AllDirectories);
foreach (var file in moduleJsonFiles)
{
var moduleInfo = JsonConvert.DeserializeObject<ModuleInfo>(File.ReadAllText(file));
if (moduleInfo != null)
{
//判断是否已存在
if (_moduleInfos.Any(m => m.Name.Equals(moduleInfo.Name)))
continue;
var assemblyHelper = new AssemblyHelper();
//此处默认模块命名空间前缀与当前项目相同
moduleInfo.AssembliesInfo = new ModuleAssembliesInfo
{
Domain = assemblyHelper.Load(m => m.Name.EndsWith("Module." + moduleInfo.Id + ".Domain")).FirstOrDefault(),
Infrastructure = assemblyHelper.Load(m => m.Name.EndsWith("Module." + moduleInfo.Id + ".Infrastructure")).FirstOrDefault(),
Application = assemblyHelper.Load(m => m.Name.EndsWith("Module." + moduleInfo.Id + ".Application")).FirstOrDefault(),
Web = assemblyHelper.Load(m => m.Name.EndsWith("Module." + moduleInfo.Id + ".Web")).FirstOrDefault(),
};
Check.NotNull(moduleInfo.AssembliesInfo.Domain, moduleInfo.Id + "模块的Domain程序集未发现");
Check.NotNull(moduleInfo.AssembliesInfo.Infrastructure, moduleInfo.Id + "模块的Infrastructure程序集未发现");
Check.NotNull(moduleInfo.AssembliesInfo.Application, moduleInfo.Id + "模块的Application程序集未发现");
Check.NotNull(moduleInfo.AssembliesInfo.Web, moduleInfo.Id + "模块的Web程序集未发现");
//加载模块初始化器
var moduleInitializerType = moduleInfo.AssembliesInfo.Web.GetTypes().FirstOrDefault(t => typeof(IModuleInitializer).IsAssignableFrom(t));
if (moduleInitializerType != null && (moduleInitializerType != typeof(IModuleInitializer)))
{
moduleInfo.Initializer = (IModuleInitializer)Activator.CreateInstance(moduleInitializerType);
}
Add(moduleInfo);
}
}
}
When starting the project, create a first ModuleCollection
instance, in its constructor loads all module information, then using a single injection mode embodiment, so that the module can always access the information in the system.
/// <summary>
/// 添加模块
/// </summary>
/// <param name="services"></param>
/// <param name="env"></param>
/// <returns></returns>
public static IModuleCollection AddModules(this IServiceCollection services, IHostingEnvironment env)
{
//创建模块集合对象
var modules = new ModuleCollection();
services.AddSingleton<IModuleCollection>(modules);
var cfgHelper = new ConfigurationHelper();
var cfg = cfgHelper.Load("module", env.EnvironmentName, true);
//通用配置
services.Configure<ModuleCommonOptions>(cfg);
foreach (var module in modules)
{
if (module == null)
continue;
services.AddApplicationServices(module);
if (module.Initializer != null)
{
module.Initializer.ConfigureServices(services);
module.Initializer.ConfigOptions(services, cfg.GetSection(module.Id));
services.AddSingleton(module);
}
}
return modules;
}
And middleware dependency injection processing module
A look at what information is contained in the module:
Module injected into two categories:
1, agreed
Each module has configuration items (Options), the entity (Entity), storage (Repository), database context (DbContext), a unit of work (UnitOfWork), Service (Service), they are a good agreement, including naming, directory , usage, etc., so users only need to follow the rules to use, do not need care inject things, they are in the system automatically injected.
In an example of data access, storage access related data (the Repository), the context database (the DbContext), a unit of work (the UnitOfWork) is performed automatically based on the configuration information and the injection module, and are in a Scoped
manner injection. Specific code to view the Data.AspNetCore
project.
2, custom
Each module may be some need to inject a unique service, these services are part of a custom requires developers to manually inject. For example,
权限管理(Admin)
the module权限验证处理(PermissionValidateHandler.cs)
, which implementsIPermissionValidateHandler
the interface, designed to do permission validation.
In addition to injection, each intermediate module has a unique and special configuration for some features, in order to integrate this information along to the project, NetModular
abstract one IModuleInitializer
interface, which includes the following four methods:
/// <summary>
/// 模块初始化器接口
/// </summary>
public interface IModuleInitializer
{
/// <summary>
/// 注入服务
/// </summary>
/// <param name="services"></param>
void ConfigureServices(IServiceCollection services);
/// <summary>
/// 配置中间件
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
void Configure(IApplicationBuilder app, IHostingEnvironment env);
/// <summary>
/// 配置MVC
/// </summary>
/// <param name="mvcOptions"></param>
void ConfigureMvc(MvcOptions mvcOptions);
/// <summary>
/// 配置选项
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
void ConfigOptions(IServiceCollection services, IConfiguration configuration);
}
Method Description:
1, ConfigureServices: for injecting Service
2, Configure: for configuring middleware
3, ConfigureMvc: used to configure the MVC-related functions
4, ConfigOptions: a configuration module for configuration items
In each module, it must include a IModuleInitializer
realization ModuleInitializer
has 权限管理(Admin)
module, for example:
public class ModuleInitializer : IModuleInitializer
{
public void ConfigureServices(IServiceCollection services)
{
//权限验证服务
services.AddScoped<IPermissionValidateHandler, PermissionValidateHandler>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}
public void ConfigureMvc(MvcOptions mvcOptions)
{
// 审计日志过滤器
mvcOptions.Filters.Add(typeof(AuditingFilter));
}
public void ConfigOptions(IServiceCollection services, IConfiguration configuration)
{
// Admin配置项
services.Configure<AdminOptions>(configuration);
}
}
When the system is started, it will be designated in step, call the corresponding methods of these modules, such as when invoked service.AddModules
during the method, the module will traverse and to inject custom services and configuration items,
public static IModuleCollection AddModules(this IServiceCollection services, IHostingEnvironment env)
{
var modules = new ModuleCollection();
services.AddSingleton<IModuleCollection>(modules);
var cfgHelper = new ConfigurationHelper();
var cfg = cfgHelper.Load("module", env.EnvironmentName, true);
services.Configure<ModuleCommonOptions>(cfg);
// 遍历模块
foreach (var module in modules)
{
if (module == null)
continue;
services.AddApplicationServices(module);
// 判断IModuleInitializer实现是否存在
if (module.Initializer != null)
{
// 注入服务
module.Initializer.ConfigureServices(services);
//配置配置项
module.Initializer.ConfigOptions(services, cfg.GetSection(module.Id));
services.AddSingleton(module);
}
}
return modules;
}
At this point, all the information modules which are integrated into the system ~
Original starter: before and after the end of the separation ASP.NET Core modular framework for rapid development of 4 introduces modular realization of ideas