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 { getset; }
  }

 

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<stringstring>
         {
             {"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);
 
     }
posted @ 2021-03-17 19:07  dreamw  阅读(197)  评论(0编辑  收藏  举报