摘要:builder.Services.Configure((options) =>{options.Name ="张三";});//获取TestOptions,输出张三{options.Name ="李四";});//获取TestOpti
hello,大家好,欢迎来到橙子老哥的分享时刻,希望大家一起学习,一起进步。
欢迎加入.net意社区,第一时间了解我们的动态,文章第一时间分享至社区
社区官方地址:chengzilaoge520本期的标题,橙子老哥先给大家卖个关子
相信大家肯定都遇到过,options选项如何在build之前获取的问题,特别是在一些模块化的操作上,例如有多个startup模块文件等a模块在Build之前要拿到b模块的Options
builder.Services.Configure((options) =>{
options.Name ="张三";
});
//获取TestOptions,输出张三
{
options.Name ="李四";
});
//获取TestOptions,输出李四
var app = builder.Build;
//常规方法,进行build,然后服务容器中获取
var name=app.Services.GetRequiredService>.Value.Name;
上面的问题,在于我们需要Build完之后,才能获取到options,但是build是一个吃性能的操作,因为会将服务容器中所有服务进行操作所以,我们能不能直接在build之前获取到TestOptions的值呢?
当然可以
2、ServiceDescriptor中的ImplementationInstance
IServiceCollectionServiceDescriptor只是存储了服务的一些信息,啥也没做,包括以下属性Lifetime(生命周期)
ServiceType(依赖注入寻找的类型)
ServiceKey (key依赖注入寻找的类型)
ImplementationType(依赖注入实现的类型)
KeyedImplementationType(key依赖注入实现的类型)
ImplementationInstance(依赖注入实现的实例)
ImplementationFactory(依赖注入实现的实例工厂)
KeyedImplementationInstance(key依赖注入实现的实例)
KeyedImplementationFactory(key依赖注入实现的实例工厂)
这里我们关注,Instance和Type的区别 像我们的单例模式,有2种方式的注入
单例的懒汉模式builder.Services.AddSingleton;
单例的饿汉模式
builder.Services.AddSingleton(new TestOptions);
在第二种方式种,我们已经实例化了TestOptions对象,所以后续可能能通过ImplementationInstance上面的操作,如果都清楚了,那我们不就可以借助把实例进行扭转了?前提是要声明好单例,那我们封装一个对象访问器
//创建一个对象访问器,包一层
public class ObjectAccessor : IObjectAccessor
{
public T? Value { get; set; }
public ObjectAccessor
{
}
public ObjectAccessor(T? obj)
{
Value = obj;
}
}
//将单实例新增到容器
public static ObjectAccessor AddObjectAccessor(this IServiceCollection services, T obj)
{
return services.AddObjectAccessor(new ObjectAccessor(obj));
}
//通过 ServiceType 去找ImplementationInstance获取对象
public static T? GetObjectOr(this IServiceCollection services)
where T : class
{
return services.GetSingletonInstanceOr>?.Value;
}
public static T? GetSingletonInstanceOr(this IServiceCollection services)
{
return (T?)services
.FirstOrDefault(d => d.ServiceType == typeof(T))
?.NormalizedImplementationInstance;
}
这样,我们每次向服务容器中新增AddObjectAccessor,直接通过GetObjectOr就可以在build之前获取到实例数据了
所以,对象访问器的作用,是给依赖注入的单例包了一层,为了方便在build之前去获取,前提:单例/实例
到了这里,思考过的小伙伴们会发现,这里的前提是单例/实例,options选项模式包在对象访问器里面,确实解决单例问题,但是实例哪里来?
Configure里面我们丢的是一个方法啊。
方法,对啊,方法难道不是一个委托对象吗?我们把多个委托当成了一个list对象依次执行不就行了?为了解决Configure不是实例,而是一个方法,我们还得再包一层
//将多个委托包装成一个对象public class PreConfigureActionList : List>
{
//依次按顺序执行委托
public void Configure(TOptions options)
{
foreach (var action in this)
{
action(options);
}
}
//反射获取单实例
public TOptions Configure
{
var options = Activator.CreateInstance;
Configure(options);
return options;
}
}
//配置options的时候,通过PreConfigure包一层
public static IServiceCollection PreConfigure(this IServiceCollection services, Action optionsAction)
{
services.GetPreConfigureActions.Add(optionsAction);
return services;
}
//第一次加入,和获取的时候,直接塞入一个IObjectAccessor单实例到对象访问器中
public static PreConfigureActionList GetPreConfigureActions(this IServiceCollection services)
{
var actionList = services.GetSingletonInstanceOr>>?.Value;
if (actionList == )
{
actionList = new PreConfigureActionList;
services.AddObjectAccessor(actionList);
}
return actionList;
}
我们使用PreConfigure
这里有关键的几点
对象访问器是为了方便使用依赖注入的单实例访问
PreConfigureActionList是为了将多个委托包装成一个单实例对象
PreConfigure就是将他们组合起来,方便使用
还有最关键的一点,允许这样玩的前提,是各个模块之间有明确的加载顺序
看到这里,恭喜你,学习到了Abp.VNextPreConfigure注入原理,在多模块中,也算是一种很有实际意义的奇淫技巧讲本期的原因,是最近很多人刚好问到我abp对象访问器到底有啥用,干脆扩展全面讲解一遍,如果你还有想研究的东西,可以加入我们一起学习哦~
哈哈哈,很多小伙伴应该似曾相识吧,我这里就不卖关子了 其实本期的标题是:《Abp.VNext PreConfigure注入的源码解读》,只是感觉很多人没有清楚这个概念,索性就换了个标题党的名字
.Net意社区,高频发布原创有深度的.Net相关知识内容
与你一起学习,一起进步
来源:opendotnet