邵阳建网站多少钱网站制作的软件有哪些
web/
2025/10/3 8:26:30/
文章来源:
邵阳建网站多少钱,网站制作的软件有哪些,有没有做cad单的网站,企业数字化平台模块化和自动服务注册 基于 ASP.NET Core 开发的 Web 框架中#xff0c;最著名的是 ABP#xff0c;ABP 主要特点之一开发不同项目(程序集)时#xff0c;在每个项目中创建一个模块类#xff0c;程序加载每个程序集中#xff0c;扫描出所有的模块类#xff0c;然后通过模块…模块化和自动服务注册 基于 ASP.NET Core 开发的 Web 框架中最著名的是 ABPABP 主要特点之一开发不同项目(程序集)时在每个项目中创建一个模块类程序加载每个程序集中扫描出所有的模块类然后通过模块类作为入口初始化程序集。 使用模块化开发程序好处是不需要关注程序集如何加载配置。开发人员开发程序集时在模块类中配置如何初始化、如何读取配置使用者只需要将模块类引入进来即可由框架自动启动模块类。 Maomi.Core 也提供了模块化开发的能力同时还包括简单易用的自动服务注册。Maomi.Core 是一个很简洁的包可以在控制台、Web 项目、WPF 项目中使用在 WPF 项目中结合 MVVM 可以大量减少代码复杂度让代码更加清晰明朗。 快速入手 有 Demo1.Api、Demo1.Application 两个项目每个项目都有一个模块类模块类需要实现 IModule 接口。 Demo1.Application 项目的 ApplicationModule.cs 文件内容如下 public class ApplicationModule : IModule{// 模块类中可以使用依赖注入private readonly IConfiguration _configuration;public ApplicationModule(IConfiguration configuration){_configuration configuration;}public void ConfigureServices(ServiceContext services){// 这里可以编写模块初始化代码}} 如果要将服务注册到容器中在 class 上加上 [InjectOn] 特性即可。 public interface IMyService{int Sum(int a, int b);}[InjectOn] // 自动注册的标记public class MyService : IMyService{public int Sum(int a, int b){return a b;}} 上层模块 Demo1.Api 中的 ApiModule.cs 可以通过特性注解引用底层模块。 [InjectModuleApplicationModule]public class ApiModule : IModule{public void ConfigureServices(ServiceContext services){// 这里可以编写模块初始化代码}} 最后在程序启动时配置模块入口并进行初始化。
var builder WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();// 注册模块化服务并设置 ApiModule 为入口
builder.Services.AddModuleApiModule();var app builder.Build(); 模块可以依赖注入 在 ASP.NET Core 配置 Host 时会自动注入一些框架依赖的服务如 IConfiguration 等因此在 .AddModuleApiModule() 开始初始化模块服务时模块获取已经注入的服务。 每个模块都需要实现 IModule 接口其定义如下 /// summary/// 模块接口/// /summarypublic interface IModule{/// summary/// 模块中的依赖注入/// /summary/// param namecontext模块服务上下文/paramvoid ConfigureServices(ServiceContext context);} 除了可以直接在模块构造函数注入服务之外还可以通过 ServiceContext context 获取服务和配置。 /// summary/// 模块上下文/// /summarypublic class ServiceContext{private readonly IServiceCollection _serviceCollection;private readonly IConfiguration _configuration;internal ServiceContext(IServiceCollection serviceCollection, IConfiguration configuration){_serviceCollection serviceCollection;_configuration configuration;}/// summary/// 依赖注入服务/// /summarypublic IServiceCollection Services _serviceCollection;/// summary/// 配置/// /summarypublic IConfiguration Configuration _configuration;} 模块化 因为模块之间会有依赖关系为了识别这些依赖关系Maomi.Core 使用树来表达依赖关系。 Maomi.Core 在启动模块服务时扫描所有模块类然后将模块依赖关系存放到模块树中然后按照左序遍历的算法对模块逐个初始化也就是先从底层模块开始进行初始化。 循环依赖检测 Maomi.Core 可以识别模块循环依赖 比如有以下模块和依赖 [InjectModuleA()]
[InjectModuleB()]
class C:IModule[InjectModuleA()]
class B:IModule// 这里出现了循环依赖
[InjectModuleC()]
class A:IModule// C 是入口模块
services.AddModuleC(); 因为 C 模块依赖 A、B 模块所以 A、B 是节点 C 的子节点而 A、B 的父节点则是 C。当把 A、B、C 三个模块以及依赖关系扫描完毕之后会得到以下的模块依赖树。 如下图所示每个模块都做了下标表示不同的依赖关系一个模块可以出现多次C1 - A0 表示 C 依赖 A。 C0 开始没有父节点则不存在循环依赖。 从 A0 开始A0 - C0 该链路中也没有出现重复的 A 模块。 从 C1 开始C1 - A0 - C0 该链路中 C 模块重复出现则说明出现了循环依赖。 从 C2 开始C2 - A1 - B0 - C0 该链路中 C 模块重复出现则说明出现了循环依赖。 模块初始化顺序 在生成模块树之后通过对模块树进行后序遍历即可。 比如有以下模块以及依赖。
[InjectModuleC()]
[InjectModuleD()]
class E:IModule[InjectModuleA()]
[InjectModuleB()]
class C:IModule[InjectModuleB()]
class D:IModule[InjectModuleA()]
class B:IModuleclass A:IModule// E 是入口模块
services.AddModuleE(); 生成模块依赖树如图所示 首先从 E0 开始扫描因为 E0 下存在子节点 C0、 D0那么就会先顺着 C0 再次扫描扫描到 A0 时因为 A0 下已经没有子节点了所以会对 A0 对应的模块 A 进行初始化。根据上图模块依赖树进行后序遍历初始化模块的顺序是已经被初始化的模块会跳过 服务自动注册 Maomi.Core 是通过 [InjectOn] 识别要注册该服务到容器中其定义如下 /// summary/// 依赖注入标记/// /summary[AttributeUsage(AttributeTargets.Class, AllowMultiple false, Inherited false)]public class InjectOnAttribute : Attribute{/// summary/// 要注入的服务/// /summarypublic Type[]? ServicesType { get; set; }/// summary/// 生命周期/// /summarypublic ServiceLifetime Lifetime { get; set; }/// summary/// 注入模式/// /summarypublic InjectScheme Scheme { get; set; }/// summary/// 是否注入自己/// /summarypublic bool Own { get; set; } false;/// summary/// /// /summary/// param namelifetime/param/// param namescheme/parampublic InjectOnAttribute(ServiceLifetime lifetime ServiceLifetime.Transient, InjectScheme scheme InjectScheme.OnlyInterfaces){Lifetime lifetime;Scheme scheme;}} 使用 [InjectOn] 时默认是注册服务为 Transient 生命周期且注册所有接口。 [InjectOn]public class MyService : IAService, IBService 等同于
services.AddTransientIAService, MyService();
services.AddTransientIBService, MyService();如果只想注册 IAService可以将注册模式设置为InjectScheme.Some 然后自定义注册的类型 [InjectOn(lifetime: ServiceLifetime.Transient,Scheme InjectScheme.Some,ServicesType new Type[] { typeof(IAService) })]public class MyService : IAService, IBService 也可以把自身注册到容器中 [InjectOn(Own true)]
public class MyService : IMyService等同于
services.AddTransientIAService, MyService();
services.AddTransientMyService(); 如果服务继承了类、接口只想注册父类那么可以这样写 public class ParentService { }[InjectOn(Scheme InjectScheme.OnlyBaseClass)]public class MyService : ParentService, IDisposable 等同于
services.AddTransientParentService, MyService();
services.AddTransientMyService();如果只注册自身忽略接口等可以使用
[InjectOn(ServiceLifetime.Scoped, Scheme InjectScheme.None, Own true)] 模块化和自动服务注册的设计和实现 在本小节中我们将会开始设计一个支持模块化和自动服务注册的小框架从设计和实现 Maomi.Core 开始我们在后面的章节中会掌握更多框架技术的设计思路和实现方法从而掌握从零开始编写一个框架的能力。 项目说明 创建一个名为 Maomi.Core 的类库项目这个类库中将会包含框架核心抽象和实现代码。 为了减少命名空间长度便于开发的时候引入需要的命名空间打开 Maomi.Core.csproj 文件在 PropertyGroup 属性中添加一行配置
RootNamespaceMaomi/RootNamespace 配置 RootNamespace 属性之后我们在 Maomi.Core 项目中创建的类型其命名空间都会以 Maomi. 开头而不是 Maomi.Core。 接着为项目添加两个依赖包以便实现自动依赖注入和初始化模块时提供配置。
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Configuration.Abstractions 模块化设计 当本章的代码编写完毕之后我们可以这样实现一个模块、初始化模块、引入依赖模块。代码示例如下 [InjectModuleApplicationModule]public class ApiModule : IModule{private readonly IConfiguration _configuration;public ApiModule(IConfiguration configuration){_configuration configuration;}public void ConfigureServices(ServiceContext context){var configuration context.Configuration;context.Services.AddCors();}} 从这段代码笔者以从上到下的顺序来解读我们需要实现哪些技术点。 1模块依赖。 [InjectModuleApplicationModule] 表示当前模块需要依赖哪些模块。如果需要依赖多个模块可以使用多个特性示例如下
[InjectModuleDomainModule]
[InjectModuleApplicationModule] 2模块接口和初始化。 每一个模块都需要实现 IModule 接口框架识别到类型继承了这个接口后才会把类型当作一个模块类进行处理。IModule 接口很简单只有 ConfigureServices(ServiceContext context) 一个方法可以在这个方法中编写初始化模块的代码。ConfigureServices 方法中有一个 ServiceContext 类型的参数 ServiceContext 中包含了 IServiceCollection、IConfiguration 模块可以从 ServiceContext 中获得当前容器的服务、启动时的配置等。 3依赖注入 每个模块的构造函数都可以使用依赖注入可以在模块类中注入需要的服务开发者可以在模块初始化时通过这些服务初始化模块。 基于以上三点我们可以先抽象出特性类、接口等由于这些类型不包含具体的逻辑因此从这一部分先下手实现起来会更简单可以避免大脑混乱编写框架时不知道要从哪里先下手。 创建一个 ServiceContext 类用于在模块间传递服务上下文信息其代码如下 public class ServiceContext{private readonly IServiceCollection _serviceCollection;private readonly IConfiguration _configuration;internal ServiceContext(IServiceCollection serviceCollection, IConfiguration configuration){_serviceCollection serviceCollection;_configuration configuration;}public IServiceCollection Services _serviceCollection;public IConfiguration Configuration _configuration;} 根据实际需求还可以在 ServiceContext 中添加日志等属性字段。 创建 IModule 接口。 public interface IModule{void ConfigureServices(ServiceContext services);} 创建 InjectModuleAttribute 特性用于引入依赖模块。 [AttributeUsage(AttributeTargets.Class, AllowMultiple true, Inherited false)]public class InjectModuleAttribute : Attribute{// 依赖的模块public Type ModuleType { get; private init; }public InjectModuleAttribute(Type type){ModuleType type;}}[AttributeUsage(AttributeTargets.Class, AllowMultiple true, Inherited false)]public sealed class InjectModuleAttributeTModule : InjectModuleAttributewhere TModule : IModule{public InjectModuleAttribute() : base(typeof(TModule)){}} 泛型特性属于 C# 11 的新语法。 定义两个特性类后我们可以使用 [InjectModule(typeof(AppModule))] 或 InjectModuleAppModule 的方式定义依赖模块。 自动服务注册的设计 当完成本章的代码编写后如果需要注入服务只需要标记 [InjectOn] 特性即可。
// 简单注册
[InjectOn]
public class MyService : IMyService
// 注注册并设置生命周期为 scope
[InjectOn(ServiceLifetime.Scoped)]
public class MyService : IMyService// 只注册接口不注册父类
[InjectOn(InjectScheme.OnlyInterfaces)]
public class MyService : ParentService, IMyService 有时我们会有各种各样的需求例如 MyService 继承了父类 ParentService 和接口 IMyService但是只需要注册 ParentService而不需要注册接口又或者只需要注册 MyService而不需要注册 ParentService 、 IMyService。 创建 InjectScheme 枚举定义注册模式 public enum InjectScheme{// 注入父类、接口Any,// 手动选择要注入的服务Some,// 只注入父类OnlyBaseClass,// 只注入实现的接口OnlyInterfaces,// 此服务不会被注入到容器中None} 定义服务注册特性 // 依赖注入标记[AttributeUsage(AttributeTargets.Class, AllowMultiple false, Inherited false)]public class InjectOnAttribute : Attribute{// 要注入的服务public Type[]? ServicesType { get; set; }// 生命周期public ServiceLifetime Lifetime { get; set; }// 注入模式public InjectScheme Scheme { get; set; }// 是否注入自己public bool Own { get; set; } false;public InjectOnAttribute(ServiceLifetime lifetime ServiceLifetime.Transient, InjectScheme scheme InjectScheme.OnlyInterfaces){Lifetime lifetime;Scheme scheme;}} 模块依赖 因为模块之间会有依赖关系因此为了生成模块树需要定义一个 ModuleNode 类表示模块节点一个 ModuleNode 实例标识一个依赖关系。 /// summary/// 模块节点/// /summaryinternal class ModuleNode{// 当前模块类型public Type ModuleType { get; set; } null!;// 链表指向父模块节点用于循环引用检测public ModuleNode? ParentModule { get; set; }// 依赖的其它模块public HashSetModuleNode? Childs { get; set; }// 通过链表检测是否出现了循环依赖public bool ContainsTree(ModuleNode childModule){if (childModule.ModuleType ModuleType) return true;if (this.ParentModule null) return false;// 如果当前模块找不到记录则向上查找return this.ParentModule.ContainsTree(childModule);}public override int GetHashCode(){return ModuleType.GetHashCode();}public override bool Equals(object? obj){if (obj null) return false;if(obj is ModuleNode module){return GetHashCode() module.GetHashCode();}return false;}} 框架在扫描所有程序集之后通过 ModuleNode 实例将所有模块以及模块依赖组成一颗模块树通过模块树来判断是否出现了循环依赖。 比如有以下模块和依赖
[InjectModuleA()]
[InjectModuleB()]
class C:IModule[InjectModuleA()]
class B:IModule// 这里出现了循环依赖
[InjectModuleC()]
class A:IModule// C 是入口模块
services.AddModuleC(); 因为 C 模块依赖 A、B 模块所以 A、B 是节点 C 的子节点而 A、B 的父节点则是 C。
C.Childs new (){ A , B}A.ParentModule C
B.ParentModule C 当把 A、B、C 三个模块以及依赖关系扫描完毕之后会得到以下的模块依赖树。一个节点即是一个 ModuleNode 实例一个模块被多次引入就会出现多次。 那么如果识别到循环依赖呢只需要调用 ModuleNode.ContainsTree()从一个 ModuleNode 实例中不断往上查找 ModuleNode.ParentModule 即可如果该链表中包含相同类型的模块即为循环依赖需要抛出异常。 比如从 C0 开始没有父节点则不存在循环依赖。 从 A0 开始A0 - C0 该链路中也没有出现重复的 A 模块。 从 C1 开始C1 - A0 - C0 该链路中 C 模块重复出现则说明出现了循环依赖。 所以是否出现了循环依赖判断起来是很简单的我们只需要从 ModuleNode.ContainsTree() 往上查找即可。 在生成模块树之后通过对模块树进行后序遍历即可。 比如有以下模块以及依赖。
[InjectModuleC()]
[InjectModuleD()]
class E:IModule[InjectModuleA()]
[InjectModuleB()]
class C:IModule[InjectModuleB()]
class D:IModule[InjectModuleA()]
class B:IModuleclass A:IModule// E 是入口模块
services.AddModuleE(); 伪代码示例如下 private static void InitModuleTree(ModuleNode moduleNode){if (moduleNode.Childs ! null){foreach (var item in moduleNode.Childs){InitModuleTree(item);}}// 如果该节点已经没有子节点// 如果模块没有处理过if (!moduleTypes.Contains(moduleNode.ModuleType)){InitInjectService(moduleNode.ModuleType);}} 未完待续...... 文章转载自痴者工良 原文链接https://www.cnblogs.com/whuanle/p/18227954 体验地址引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/86117.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!