【橙子老哥】.NetCore 震惊!Configure选项在Build之前获取

摘要: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

相关推荐