07-003 Options 之 OptionsServiceCollectionExtensions
AddOptions 方法:
1
2
3
4
5
6
|
public static IServiceCollection AddOptions([NotNull] this IServiceCollection services, IConfiguration config = null ) { var describe = new ServiceDescriber(config); services.TryAdd(describe.Singleton( typeof (IOptions<>), typeof (OptionsManager<>))); return services; } |
这里是可以在配置文件里用自己实现的类来替换 OptionsManager<> 的。
看一下:ConfigureOptions 方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
private static bool IsAction(Type type) { return (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof (Action<>)); } private static IEnumerable<Type> FindIConfigureOptions(Type type) { var serviceTypes = type.GetTypeInfo().ImplementedInterfaces .Where(t => t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == typeof (IConfigureOptions<>)); if (!serviceTypes.Any()) { string error = "TODO: No IConfigureOptions<> found." ; if (IsAction(type)) { error += " did you mean Configure(Action<T>)" ; } throw new InvalidOperationException(error); } return serviceTypes; } public static IServiceCollection ConfigureOptions([NotNull] this IServiceCollection services, Type configureType) { var serviceTypes = FindIConfigureOptions(configureType); /* FindIConfigureOptions 找到实现的接口中是 typeof(IConfigureOptions<>) 的集合; 比如: public class FakeOptionsSetupA : ConfigureOptions<FakeOptions>{} services.ConfigureOptions(typeof(FakeOptionsSetupA)); 此时: serviceTypes=IEnumerable<Type>{ typeof(IConfigureOptions<FakeOptions>) }; 导致结果:services.AddTransient(typeof(IConfigureOptions<FakeOptions>), FakeOptionsSetupA); 当使用 IOption<FakeOptions> 的时候 具体调用的方法:GetNamedOptions(""); 实现类 OptionsManager(IEnumerable<IConfigureOptions<TOptions>> setups) 里面就添加了 FakeOptionsSetupA */ foreach ( var serviceType in serviceTypes) { services.AddTransient(serviceType, configureType); } return services; } |
这个是实例化的:
1
2
3
4
5
6
7
8
9
|
public static IServiceCollection ConfigureOptions([NotNull] this IServiceCollection services, [NotNull] object configureInstance) { var serviceTypes = FindIConfigureOptions(configureInstance.GetType()); foreach ( var serviceType in serviceTypes) { services.AddInstance(serviceType, configureInstance); } return services; } |
Configure 方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public static IServiceCollection Configure<TOptions>([NotNull] this IServiceCollection services, [NotNull] Action<TOptions> setupAction, string optionsName) { return services.Configure(setupAction, OptionsConstants.DefaultOrder, optionsName); } public static IServiceCollection Configure<TOptions>([NotNull] this IServiceCollection services, [NotNull] Action<TOptions> setupAction, int order = OptionsConstants.DefaultOrder, string optionsName = "" ) { /* 这里调用了上文中的: ConfigureOptions([NotNull]this IServiceCollection services, [NotNull]object configureInstance) 具体的区别仅在于 services.AddInstance(serviceType, configureInstance); 上面是 Transient 这种实例化的是 Singleton ConfigureOptions 方法中 具体的 action order name 写在类中 Configure 方法:直接作为方法的参数来使用 生成 ConfigureOptions 类来包装。 */ services.ConfigureOptions( new ConfigureOptions<TOptions>(setupAction) { Name = optionsName, Order = order }); return services; } |
最后时多了一个参数的 IConfiguration config 的 Configure方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public static IServiceCollection Configure<TOptions>([NotNull] this IServiceCollection services, [NotNull] IConfiguration config, int order = OptionsConstants.ConfigurationOrder, string optionsName = "" ) { // 相当于 Action 为 读取指定的配置文件信息 赋值给类 前提是字段要匹配 这里的Order 默认为 -1000 // 暗含 先读取配置文件 再调用其它指定方法的 意思 // 最终的结果 都是要 services.AddTransient(typeof(IConfigureOptions<FakeOptions>), FakeOptionsSetupA); // 或者 services.AddInstance(serviceType, configureInstance); // 供 调用 IOption<TOptions>.GetNamedOptions("") 时来使用 services.ConfigureOptions( new ConfigureFromConfigurationOptions<TOptions>(config) { Name = optionsName, Order = order }); return services; } |
看一下测试代码:
1
2
3
4
5
6
7
8
9
|
public class FakeOptions { public FakeOptions() { Message = "" ; } public string Message { get ; set ; } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
[Fact] public void SetupCallsSortedInOrder() { var services = new ServiceCollection().AddOptions(); var dic = new Dictionary< string , string > { { "Message" , "!" }, }; var config = new Configuration { new MemoryConfigurationSource(dic) }; services.Configure<FakeOptions>(o => o.Message += "Igetstomped" , -100000); services.Configure<FakeOptions>(config); // order=-1000 ; o.Message="!"; ReadProperties services.Configure<FakeOptions>(o => o.Message += "a" , -100); // -100; o.Message += "a"; services.ConfigureOptions<FakeOptionsSetupC>(); // 1000; +="C"; services.ConfigureOptions( new FakeOptionsSetupB()); // 10; +="B"; services.ConfigureOptions( typeof (FakeOptionsSetupA)); //-1; +="A"; services.Configure<FakeOptions>(o => o.Message += "z" , 10000); // 10000; +="z"; var service = services.BuildServiceProvider().GetService<IOptions<FakeOptions>>(); Assert.NotNull(service); var options = service.Options; Assert.NotNull(options); // 按照顺序从小到大 依次调用 Action 方法: /* -100000; +="Igetstomped"; -1000; ="!"; -100; +="a"; -1 ; +="A"; 10 ; +="B"; 1000; +="C"; 10000; +="z"; 结果为: "!aABCz"; */ Assert.Equal( "!aABCz" , options.Message); } |