1 Core.Infrastructure.IEngine
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Core.Infrastructure
{
///
/// 【引擎--接口】
///
/// 摘要:
/// 继承于该接口的具体要求实现类实现了以下功能:
/// 1、“Engine”(引擎)实例用于对.NetCore框架内置依赖注入容器接口实例所存储的当前程序中的具体现类和中间件的实例进行集中管理。
/// 2、“Engine”(引擎)实例用于对当前程序中的自定义管道中间件实例进行集中管理。
/// 3、“Engine”(引擎)实例用于对服务提供程序接口进行实例化,通过该实例的“GetRequiredService”或“GetService”方法从内置依赖注入容器中获取1个指定接口/类的1/n实例(注意:1个指定接口/类的实例必须已经注入到了内置依赖注入容器中)。
/// 4、“Engine”(引擎)实例是单例的,即在程序运行过程中“Engine”实例是唯一的,该实例,在运行过程序中只能被替换(同内存的1个引用地址),而不能再次被实例化1次(两个不内存的引用地址)。
///
///
public interface IEngine
{
#region 方法
/// name="services">.NetCore框架内置依赖注入容器接口实例。
/// name="configuration">.NetCore框架内置配置接口实例(存储着当前程序中所有*.json文件中的数据)。
///
/// 【配置服务】
///
/// 摘要:
/// 把具体现类和中间件注入到内置依赖注入容器后,把“Engine”实例存储到单例实例的字典成员实例中,同时把.NetCore框架内置依赖注入容器接口实例所存储的当前程序中的具体现类和中间件的实例通过“Engine”(引擎)单例实例存储到单例类的字典属性成员实例中。
/// 说明:
/// 1、“Engine”实例用于对.NetCore框架内置依赖注入容器接口实例所存储的当前程序中的具体现类和中间件的实例进行集中管理。
/// 2、“Engine”实例是单例的,即在程序运行过程中“Engine”实例是唯一的,该实例,在运行过程序中只能被替换(同内存的1个引用地址),而不能再次被实例化1次(两个不内存的引用地址)。
///
///
void ConfigureServices(IServiceCollection services, IConfiguration configuration);
///
/// name="scope">作用域服务接口实例,用于从内置依赖注入容器中获取生命周期被注册为“AddScoped”的具体实现类的实例,默认值:null,即在默认状态下只能获取生命周期被注册为“AddSingleton/AddTransient”的具体实现类的实例。
///
/// 【解析】
///
/// 摘要:
/// 通过“GetService”方法从内置依赖注入容器中获取1个指定接口/类的1实例(注意:1个指定接口/类的实例必须已经注入到了内置依赖注入容器中)。
///
///
/// 返回:
/// 1个指定接口/类的1实例。
///
///
T Resolve<T>(IServiceScope scope = null) where T : class;
/// name="type">1个指定类/接口的类型实例
/// name="scope">作用域服务接口实例,用于从内置依赖注入容器中获取生命周期被注册为“AddScoped”的具体实现类的实例,默认值:null,即在默认状态下只能获取生命周期被注册为“AddSingleton/AddTransient”的具体实现类的实例。
///
/// 【解析】
///
/// 摘要:
/// 通过“GetService”方法从内置依赖注入容器中获取1个指定接口/类的1实例(注意:1个指定接口/类的实例必须已经注入到了内置依赖注入容器中)。
///
///
/// 返回:
/// 1个指定接口/类的1实例。
///
///
object Resolve(Type type, IServiceScope scope = null);
///
///
/// 【解析全部】
///
/// 摘要:
/// 通过“GetService”方法从内置依赖注入容器中获取1个指定接口/类的1/n实例(注意:1个指定接口/类的实例必须已经注入到了内置依赖注入容器中),把并这些实例存储到可枚举实例中。
///
///
/// 返回:
/// 可枚举实例。该实例存储着1个指定接口/类的1/n实例。
///
///
IEnumerable<T> ResolveAll<T>();
/// name="type">1个指定的类/接口的类型实例。
///
/// 【未注入解析】
///
/// 摘要:
/// 如果1指定的类/接口未注入到内置依赖注入容器中,通过该方法以反射方式获取1指定的类/接口的1/n个实例。
///
///
/// 返回:
/// 1指定的类/接口的1/n个实例。
///
///
object ResolveUnregistered(Type type);
#endregion
}
}
2 Core.Infrastructure.Engine
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Core.Infrastructure
{
///
/// 【引擎--类】
///
/// 摘要:
/// 1、“Engine”(引擎)实例用于对.NetCore框架内置依赖注入容器接口实例所存储的当前程序中的具体现类和中间件的实例进行集中管理。
/// 2、“Engine”(引擎)实例用于对当前程序中的自定义管道中间件实例进行集中管理。
/// 3、“Engine”(引擎)实例用于对服务提供程序接口进行实例化,通过该实例的“GetRequiredService”或“GetService”方法从内置依赖注入容器中获取1个指定接口/类的1/n实例(注意:1个指定接口/类的实例必须已经注入到了内置依赖注入容器中)。
/// 4、“Engine”(引擎)实例是单例的,即在程序运行过程中“Engine”实例是唯一的,该实例,在运行过程序中只能被替换(同内存的1个引用地址),而不能再次被实例化1次(两个不内存的引用地址)。
///
///
public class Engine : IEngine
{
#region 属性
///
/// 【服务提供程序】
///
/// 摘要:
/// 服务提供程序接口实例,通过该实例的“GetRequiredService”或“GetService”方法从内置依赖注入容器中获取1个指定接口/类的1/n实例(注意:1个指定接口/类的实例必须已经注入到了内置依赖注入容器中)。
///
///
public virtual IServiceProvider ServiceProvider { get; protected set; }
#endregion
#region 方法--私有/保护
/// name="scope">作用域服务接口实例,用于从内置依赖注入容器中获取生命周期被注册为“AddScoped”的具体实现类的实例,默认值:null,即在默认状态下只获取取生命周期被注册为“AddSingleton/AddTransient”的具体实现类的实例。
///
/// 【获取服务提供程序】
///
/// 摘要:
/// 获取服务提供程序接口实例,通过该实例的“GetRequiredService”或“GetService”方法从内置依赖注入容器中获取1个指定接口/类的1/n实例(注意:1个指定接口/类的实例必须已经注入到了内置依赖注入容器中)。
///
///
/// 返回:
/// 服务提供程序接口实例,为获取具体实现类的实例提供支撑。
///
///
protected virtual IServiceProvider GetServiceProvider(IServiceScope scope = null)
{
if (scope == null)
{
var accessor = ServiceProvider?.GetService<IHttpContextAccessor>();
var context = accessor?.HttpContext;
//如果作用域服务接口实例存在,则返回“AddScoped”服务提供程序;如果作用域服务接口实例不存在,则返回生命周期被注册为“AddSingleton/AddTransient”的服务提供程序。
return context?.RequestServices ?? ServiceProvider;
}
return scope.ServiceProvider;
}
#endregion
#region 方法--接口实现
/// name="services">.NetCore框架内置依赖注入容器接口实例。
/// name="configuration">.NetCore框架内置配置接口实例(存储着当前程序中所有*.json文件中的数据)。
///
/// 【配置服务】
///
/// 摘要:
/// 把具体现类和中间件注入到内置依赖注入容器后,把“Engine”实例存储到单例实例的字典成员实例中,同时把.NetCore框架内置依赖注入容器接口实例所存储的当前程序中的具体现类和中间件的实例通过“Engine”(引擎)单例实例存储到单例类的字典属性成员实例中。
/// 说明:
/// 1、“Engine”实例用于对.NetCore框架内置依赖注入容器接口实例所存储的当前程序中的具体现类和中间件的实例进行集中管理。
/// 2、“Engine”实例是单例的,即在程序运行过程中“Engine”实例是唯一的,该实例,在运行过程序中只能被替换(同内存的1个引用地址),而不能再次被实例化1次(两个不内存的引用地址)。
///
///
public virtual void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
//把“Engine”实例存储到单例实例的字典成员实例中。
services.AddSingleton<IEngine>(this);
}
///
/// name="scope">作用域服务接口实例,用于从内置依赖注入容器中获取生命周期被注册为“AddScoped”的具体实现类的实例,默认值:null,即在默认状态下只能获取生命周期被注册为“AddSingleton/AddTransient”的具体实现类的实例。
///
/// 【解析】
///
/// 摘要:
/// 通过“GetService”方法从内置依赖注入容器中获取1个指定接口/类的1实例(注意:1个指定接口/类的实例必须已经注入到了内置依赖注入容器中)。
///
///
/// 返回:
/// 1个指定接口/类的1实例。
///
///
public virtual T Resolve<T>(IServiceScope scope = null) where T : class
{
return (T)Resolve(typeof(T), scope);
}
/// name="type">1个指定类/接口的类型实例
/// name="scope">作用域服务接口实例,用于从内置依赖注入容器中获取生命周期被注册为“AddScoped”的具体实现类的实例,默认值:null,即在默认状态下只能获取生命周期被注册为“AddSingleton/AddTransient”的具体实现类的实例。
///
/// 【解析】
///
/// 摘要:
/// 通过“GetService”方法从内置依赖注入容器中获取1个指定接口/类的1实例(注意:1个指定接口/类的实例必须已经注入到了内置依赖注入容器中)。
///
///
/// 返回:
/// 1个指定接口/类的1实例。
///
///
public virtual object Resolve(Type type, IServiceScope scope = null)
{
return GetServiceProvider(scope)?.GetService(type);
}
///
///
/// 【解析全部】
///
/// 摘要:
/// 通过“GetService”方法从内置依赖注入容器中获取1个指定接口/类的1/n实例(注意:1个指定接口/类的实例必须已经注入到了内置依赖注入容器中),把并这些实例存储到可枚举实例中。
///
///
/// 返回:
/// 可枚举实例。该实例存储着1个指定接口/类的1/n实例。
///
///
public virtual IEnumerable<T> ResolveAll<T>()
{
return (IEnumerable<T>)GetServiceProvider().GetServices(typeof(T));
}
/// name="type">1个指定的类/接口的类型实例。
///
/// 【未注入解析】
///
/// 摘要:
/// 如果1指定的类/接口未注入到内置依赖注入容器中,通过该方法以反射方式获取1指定的类/接口的1/n个实例。
///
///
/// 返回:
/// 1指定的类/接口的1/n个实例。
///
///
public virtual object ResolveUnregistered(Type type)
{
Exception innerException = null;
//尝试通过指定类的拷贝构造方法,获取该指定类的实例。
foreach (var constructor in type.GetConstructors())
{
try
{
//依次获取拷贝构造方法中所需参数实例。
var parameters = constructor.GetParameters().Select(parameter =>
{
var service = Resolve(parameter.ParameterType);
//如果拷贝构造方法中所需参数实例的实例值为:null(即参数实例,无实例值),则直接抛出异常信息。
if (service == null)
throw new NopException("该拷贝构造方法中有参数的实例值为:null");
return service;
});
//1指定的类/接口的类型实例及其所需的参数实例,以反射方式获取1指定的类/接口的1/n个实例。
return Activator.CreateInstance(type, parameters.ToArray());
}
catch (Exception ex)
{
innerException = ex;
}
}
throw new NopException("未能找指定类/接口的拷贝构造方法。", innerException);
}
#endregion
}
}
3 Core.Infrastructure.EngineContext
using System.Runtime.CompilerServices;
namespace Core.Infrastructure
{
///
/// 【引擎上下文--类】
///
/// 摘要:
/// 实例化1个不受多线程操作影响的引擎接口单例实例(“Engine”(引擎)实例),并把该实例存储到单例类的字典属性成员实例中(副本/拷贝)。
///
///
public class EngineContext
{
#region 属性
///
/// 【当前】
///
/// 摘要:
/// 把1个不受多线程操作影响的引擎接口单例实例(“Engine”(引擎)实例)存储到单例类的字典属性成员实例中(副本/拷贝)。
///
///
public static IEngine Current
{
get
{
if (Singleton<IEngine>.Instance == null)
{
Create();
}
return Singleton<IEngine>.Instance;
}
}
#endregion
#region 方法
///
/// 【创建】
///
/// 摘要:
/// 实例化1个不受多线程操作影响的引擎接口单例实例(“Engine”(引擎)实例),即在程序运行过程中“Engine”实例是唯一的,该实例,在运行过程序中只能被替换(同内存的1个引用地址),而不能再次被实例化1次(两个不内存的引用地址)。
/// MethodImpl:
/// 通过“[MethodImpl(MethodImplOptions.Synchronized)]”标注,来确保在多线程操作不会对该单例实例产生影响,
/// 如果不使用“[MethodImpl(MethodImplOptions.Synchronized)]”标注,在多线程状态下也可能创建多个不同的“IEngine”单例实例,
/// 这样违反了“IEngine”实例被定义为单例实例且不受(在同一时间内)多线程操作影响的初衷。
/// [MethodImpl(MethodImplOptions.Synchronized)]、lock(this)与lock(typeof(...)):
/// 对于稍微有点经验的.NET开发人员来说,倘若被问及如何保持线程同步,
/// 我想很多人都能说好好几种。在众多的线程同步的可选方式中,加锁无疑是最为常用的。
/// 如果仅仅是基于方法级别的线程同步,
/// 使用System.Runtime.CompilerServices.MethodImplAttribute无疑是最为简洁的一种方式。
/// MethodImplAttribute可以用于instance method,
/// 也可以用于static method。当在某个方法上标注了MethodImplAttribute,
/// 并指定MethodImplOptions.Synchronized参数,
/// 可以确保在不同线程中运行的该方式以同步的方式运行。
/// 我们几天来讨论MethodImplAttribute(MethodImplOptions.Synchronized)和lock的关系。
///
///
/// 返回:
/// 1个不受多线程操作影响的引擎接口单例实例(“Engine”(引擎)实例),即在程序运行过程中“Engine”实例是唯一的,该实例,在运行过程序中只能被替换(同内存的1个引用地址),而不能再次被实例化1次(两个不内存的引用地址)。
///
///
[MethodImpl(MethodImplOptions.Synchronized)]
public static IEngine Create()
{
//实例化1个不受多线程操作影响的引擎接口单例实例(“Engine”(引擎)实例),并把引擎接口单例实例(“Engine”(引擎)实例)存储到单例类的字典属性成员实例中(副本/拷贝),最后返回引擎接口单例实例(“Engine”(引擎)实例)。
return Singleton<IEngine>.Instance ?? (Singleton<IEngine>.Instance = new Engine());
}
///
/// 【替换】
/// name="engine">一个新构建的不受多线程操作影响的引擎接口单例实例。
///
/// 摘要:
/// 重新对存储在单例类的属性成员实例中的引擎接口单例实例(“Engine”(引擎)实例)进行覆盖,注意:这里的覆盖是针对单例类的字典属性成员实例中的引擎接口单例实例(副本/拷贝),而非擎接口单例实例(“Engine”(引擎)实例)本身,不是通过new关键字再实例化1个新的“Engine”(引擎)实例。
/// 说明:
/// 将一个新构建的“Engine”单例实例覆盖原有的程序中原有正在使用“Engine”单例实例,
/// 因为在同一次程序的执行过程中原有“Engine”单例实例的生命周期存在于在整个执行过程中,
/// 所有的操作使用的都是同一个实例,且该实例不能覆盖;而该方法却显式定义了对原有“Engine”单例实例的覆盖操作。
/// (这样显而易见同样违反了“Engine”单例实例被定义为单例实例且不受(在同一时间内)多线程操作影响的初衷,实质上该方法的功能是对该类功能下定义的否定),
/// 只有当开发者确认在极其极端情况下确且的需要使用一个新的“Engine”单例实例替换(覆盖)原有的“NEngine”单例实例的时候,才能够调用该方法。
///
///
public static void Replace(IEngine engine)
{
Singleton<IEngine>.Instance = engine;
}
#endregion
}
}
4 Framework.Infrastructure.Extensions.ServiceCollectionExtensions. AddHttpContextAccessor
/// name="services">.Net(Core)框架内置依赖注入容器实例。
///
/// 【添加HTTP上下文访问器】
///
/// 摘要:
/// 1、为从内置依赖注入容器中获取生命周期被注册为“AddScoped”的具体实现类的实例提供支撑。
/// 2、在“应/答”操作中从指定的页面获取数据,为当前页面或新页面的渲染显示提供数据支撑,
///
///
public static void AddHttpContextAccessor(this IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
5Framework.Infrastructure.Extensions.ServiceCollectionExtensions. ConfigureApplicationServices
/// name="services">.Net(Core)框架内置依赖注入容器实例。
/// name="builder">Web应用构建器的1个指定实例(Web应用构建器主要对基于.Net(Core)框架中的配置文件(*.json)进行读写操作,>=Net6)
///
/// 【配置应用程序服务】
///
/// 摘要:
/// 1、为从内置依赖注入容器中获取生命周期被注册为“AddScoped”的具体实现类的实例提供支撑。
/// 2、在“应/答”操作中从指定的页面获取数据,为当前页面或新页面的渲染显示提供数据支撑,
///
///
public static void ConfigureApplicationServices(this IServiceCollection services, WebApplicationBuilder builder)
{
//把HTTP上下文访问器实例,注入到内置容器实例中。
services.AddHttpContextAccessor();
//实例化“Engine”(引擎)单例实例。
var engine = EngineContext.Create();
//把.NetCore框架内置依赖注入容器接口实例所存储的当前程序中的具体现类和中间件的实例通过“Engine”(引擎)单例实例存储到单例类的字典属性成员实例中。
engine.ConfigureServices(services, builder.Configuration);
}
6 重构Program.cs文件
var builder = WebApplication.CreateBuilder(args);
//如果启动项中不存在“appsettings.json”文件,则通过.Net(Core)的内置方法自动新建“appsettings.json”文件。
builder.Configuration.AddJsonFile("appsettings.json", true, true);
//把当前程序中所有继承了“IConfig”接口的具体实现类的实例,依赖注入到.Net(Core)内置依赖注入容器实例中,如果需要并把这些数据持久化存储到"appsettings.json"文件。
builder.Services.ConfigureApplicationSettings(builder);
builder.Services.AddScoped<INopFileProvider, NopFileProvider>();
//抽离“EntityFrameworkCore”中间件实例的依赖注入操作,为当前程序通过“EntityFrameworkCore”中间件实例与指定数据库软件中指定数据库的CURD交互操作,提供实例支持。
builder.Services.AddEFCoreContext();
//把具体现类和中间件注入到内置依赖注入容器后,并把.NetCore框架内置依赖注入容器接口实例所存储的当前程序中的具体现类和中间件的实例通过“Engine”(引擎)单例实例存储到单例类的字典属性成员实例中。
builder.Services.ConfigureApplicationSettings(builder);
对以上功能更为具体实现和注释见230117_014shopDemo(内置容器与管道实例集中管理之IEngine)。