标签:服务 技术 exce 默认 tor vat amp enum sam
Nuget:以Microsoft.Extensins.Options开头的nuget包
Github地址:https://github.com/dotnet/extensions/tree/master/src/Options
首先看下接口
IOptions依赖于服务的依赖注入
首先看下我们需要注册的服务
public static IServiceCollection AddOptions(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions), typeof(OptionsManager)));
services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot), typeof(OptionsManager)));
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor), typeof(OptionsMonitor)));
services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory), typeof(OptionsFactory)));
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache), typeof(OptionsCache)));
return services;
}
接下来看下各个接口的具体实现:
一、OptionsManager类:
///
/// Implementation of and .
///
/// Options type.
public class OptionsManager : IOptions, IOptionsSnapshotwhere TOptions : class, new()
{
private readonly IOptionsFactory _factory;
private readonly OptionsCache _cache = new OptionsCache(); // Note: this is a private cache
///
/// Initializes a new instance with the specified options configurations.
///
/// The factory to use to create options.
public OptionsManager(IOptionsFactory factory)
{
_factory = factory;
}
///
/// The default configured instance, equivalent to Get(Options.DefaultName).
///
public TOptions Value
{
get
{
return Get(Options.DefaultName);
}
}
///
/// Returns a configured instance with the given .
///
public virtual TOptions Get(string name)
{
name = name ?? Options.DefaultName;
// Store the options in our instance cache
return _cache.GetOrAdd(name, () => _factory.Create(name));
}
}
该类实现IOptions, IOptionsSnapshot接口,当我们注入具体的选项时,依赖注入会解析到对应的OptionsManager实现。
OptionsCache选项的缓存
public class OptionsCache : IOptionsMonitorCachewhere TOptions : class
{
private readonly ConcurrentDictionarystring, Lazy> _cache = new ConcurrentDictionarystring, Lazy>(StringComparer.Ordinal);
///
/// Clears all options instances from the cache.
///
public void Clear() => _cache.Clear();
///
/// Gets a named options instance, or adds a new instance created with .
///
/// The name of the options instance.
/// The func used to create the new instance.
/// The options instance.
public virtual TOptions GetOrAdd(string name, Func createOptions)
{
if (createOptions == null)
{
throw new ArgumentNullException(nameof(createOptions));
}
name = name ?? Options.DefaultName;
return _cache.GetOrAdd(name, new Lazy(createOptions)).Value;
}
///
/// Tries to adds a new option to the cache, will return false if the name already exists.
///
/// The name of the options instance.
/// The options instance.
/// Whether anything was added.
public virtual bool TryAdd(string name, TOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
name = name ?? Options.DefaultName;
return _cache.TryAdd(name, new Lazy(() => options));
}
///
/// Try to remove an options instance.
///
/// The name of the options instance.
/// Whether anything was removed.
public virtual bool TryRemove(string name)
{
name = name ?? Options.DefaultName;
return _cache.TryRemove(name, out var ignored);
}
}
IOptionsFactory接口的实现OptionsFactory
///
/// Implementation of .
///
/// The type of options being requested.
public class OptionsFactory : IOptionsFactorywhere TOptions : class, new()
{
private readonly IEnumerable> _setups;
private readonly IEnumerable> _postConfigures;
private readonly IEnumerable> _validations;
///
/// Initializes a new instance with the specified options configurations.
///
/// The configuration actions to run.
/// The initialization actions to run.
public OptionsFactory(IEnumerable> setups, IEnumerable> postConfigures) : this(setups, postConfigures, validations: null)
{ }
///
/// Initializes a new instance with the specified options configurations.
///
/// The configuration actions to run.
/// The initialization actions to run.
/// The validations to run.
public OptionsFactory(IEnumerable> setups, IEnumerable> postConfigures, IEnumerable> validations)
{
_setups = setups;
_postConfigures = postConfigures;
_validations = validations;
}
///
/// Returns a configured instance with the given .
///
public TOptions Create(string name)
{
var options = new TOptions();
foreach (var setup in _setups)
{
if (setup is IConfigureNamedOptions namedSetup)
{
namedSetup.Configure(name, options);
}
else if (name == Options.DefaultName)
{
setup.Configure(options);
}
}
foreach (var post in _postConfigures)
{
post.PostConfigure(name, options);
}
if (_validations != null)
{
var failures = new Liststring>();
foreach (var validate in _validations)
{
var result = validate.Validate(name, options);
if (result.Failed)
{
failures.AddRange(result.Failures);
}
}
if (failures.Count > 0)
{
throw new OptionsValidationException(name, typeof(TOptions), failures);
}
}
return options;
}
}
可以看到OptionsFactory的构造函数接收三个参数:IEnumerable> 、IEnumerable>、IEnumerable>
当依赖注入容器解析OptionsFactory实例时,会找到所有实现IConfigureOptions、IPostConfigureOptions、IValidateOptions的对象
再看下TOptions Create(string name)方法
先遍历IEnumerable>
如果对象实现了IConfigureNamedOptions接口,则调用Configure(name, options)方法,否则判断name == Options.DefaultName是否为true,如果是true,则调用Configure(options)方法
再遍历IEnumerable>
直接调用PostConfigure(name, options)方法
再遍历IEnumerable>方法,主要用于验证结果是否正确,如果验证失败,则抛出异常。
以上是创建IOptions的一个流程
再来看下IOptionsMonitor接口,其实现为OptionsMonitor
OptionsMonitor构造函数有三个:IOptionsFactory、IEnumerable>、IOptionsMonitorCache
其中第一和第三个都是系统默认注册好了
第二个参数,如果用户已经注册对应的服务,则遍历该集合,监听选项的变化
IOptionsMonitor可以监听选项的变化而重新获取值
再来看下一些扩展方法
///
/// Registers an action used to configure a particular type of options.
/// Note: These are run before all .
///
/// The options type to be configured.
/// The to add the services to.
/// The action used to configure the options.
/// The so that additional calls can be chained.
public static IServiceCollection Configure(this IServiceCollection services, Action configureOptions) where TOptions : class
=> services.Configure(Options.Options.DefaultName, configureOptions);
///
/// Registers an action used to configure a particular type of options.
/// Note: These are run before all .
///
/// The options type to be configured.
/// The to add the services to.
/// The name of the options instance.
/// The action used to configure the options.
/// The so that additional calls can be chained.
public static IServiceCollection Configure(this IServiceCollection services, string name, Action configureOptions)
where TOptions : class
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
}
services.AddOptions();
services.AddSingleton>(new ConfigureNamedOptions(name, configureOptions));
return services;
}
///
/// Registers an action used to configure all instances of a particular type of options.
///
/// The options type to be configured.
/// The to add the services to.
/// The action used to configure the options.
/// The so that additional calls can be chained.
public static IServiceCollection ConfigureAll(this IServiceCollection services, Action configureOptions) where TOptions : class
=> services.Configure(name: null, configureOptions: configureOptions);
///
/// Registers an action used to initialize a particular type of options.
/// Note: These are run after all .
///
/// The options type to be configured.
/// The to add the services to.
/// The action used to configure the options.
/// The so that additional calls can be chained.
public static IServiceCollection PostConfigure(this IServiceCollection services, Action configureOptions) where TOptions : class
=> services.PostConfigure(Options.Options.DefaultName, configureOptions);
///
/// Registers an action used to configure a particular type of options.
/// Note: These are run after all .
///
/// The options type to be configure.
/// The to add the services to.
/// The name of the options instance.
/// The action used to configure the options.
/// The so that additional calls can be chained.
public static IServiceCollection PostConfigure(this IServiceCollection services, string name, Action configureOptions)
where TOptions : class
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
}
services.AddOptions();
services.AddSingleton>(new PostConfigureOptions(name, configureOptions));
return services;
}
///
/// Registers an action used to post configure all instances of a particular type of options.
/// Note: These are run after all .
///
/// The options type to be configured.
/// The to add the services to.
/// The action used to configure the options.
/// The so that additional calls can be chained.
public static IServiceCollection PostConfigureAll(this IServiceCollection services, Action configureOptions) where TOptions : class
=> services.PostConfigure(name: null, configureOptions: configureOptions);
///
/// Registers a type that will have all of its I[Post]ConfigureOptions registered.
///
/// The type that will configure options.
/// The to add the services to.
/// The so that additional calls can be chained.
public static IServiceCollection ConfigureOptions(this IServiceCollection services) where TConfigureOptions : class
=> services.ConfigureOptions(typeof(TConfigureOptions));
private static bool IsAction(Type type)
=> (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Action));
private static IEnumerable FindIConfigureOptions(Type type)
{
var serviceTypes = type.GetTypeInfo().ImplementedInterfaces
.Where(t => t.GetTypeInfo().IsGenericType &&
(t.GetGenericTypeDefinition() == typeof(IConfigureOptions)
|| t.GetGenericTypeDefinition() == typeof(IPostConfigureOptions)));
if (!serviceTypes.Any())
{
throw new InvalidOperationException(
IsAction(type)
? Resources.Error_NoIConfigureOptionsAndAction
: Resources.Error_NoIConfigureOptions);
}
return serviceTypes;
}
///
/// Registers a type that will have all of its I[Post]ConfigureOptions registered.
///
/// The to add the services to.
/// The type that will configure options.
/// The so that additional calls can be chained.
public static IServiceCollection ConfigureOptions(this IServiceCollection services, Type configureType)
{
services.AddOptions();
var serviceTypes = FindIConfigureOptions(configureType);
foreach (var serviceType in serviceTypes)
{
services.AddTransient(serviceType, configureType);
}
return services;
}
///
/// Registers an object that will have all of its I[Post]ConfigureOptions registered.
///
/// The to add the services to.
/// The instance that will configure options.
/// The so that additional calls can be chained.
public static IServiceCollection ConfigureOptions(this IServiceCollection services, object configureInstance)
{
services.AddOptions();
var serviceTypes = FindIConfigureOptions(configureInstance.GetType());
foreach (var serviceType in serviceTypes)
{
services.AddSingleton(serviceType, configureInstance);
}
return services;
}
///
/// Gets an options builder that forwards Configure calls for the same to the underlying service collection.
///
/// The options type to be configured.
/// The to add the services to.
/// The so that configure calls can be chained in it.
public static OptionsBuilder AddOptions(this IServiceCollection services) where TOptions : class
=> services.AddOptions(Options.Options.DefaultName);
///
/// Gets an options builder that forwards Configure calls for the same named to the underlying service collection.
///
/// The options type to be configured.
/// The to add the services to.
/// The name of the options instance.
/// The so that configure calls can be chained in it.
public static OptionsBuilder AddOptions(this IServiceCollection services, string name)
where TOptions : class
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.AddOptions();
return new OptionsBuilder(services, name);
}
netcore3.0 IOptions 选项(一)
标签:服务 技术 exce 默认 tor vat amp enum sam
原文地址:https://www.cnblogs.com/lanpingwang/p/12540260.html