[ASP.NET Core 3框架揭秘] Options[4]: Options模型[下篇]

六、IOptionsMonitorCache<TOptions>

IOptionsFactory<TOptions>解决了Options的创建与初始化问题,但由于它自身是无状态的,所以Options模型对Options对象实施缓存可以获得更好的性能。Options模型中针对Options对象的缓存由IOptionsMonitorCache<TOptions>对象来完成,如下所示的代码片段是该接口的定义。

public interface IOptionsMonitorCache<TOptions> where TOptions : class
{
    TOptions GetOrAdd(string name, Func<TOptions> createOptions);
    bool TryAdd(string name, TOptions options);
    bool TryRemove(string name);
    void Clear();
}

由于Options模型总是根据名称来提供对应的Options对象,所以IOptionsMonitorCache<TOptions>对象也根据名称来缓存Options对象。如上面的代码片段所示,IOptionsMonitorCache<TOptions>接口提供了4个方法,分别实现针对Options缓存的获取、添加、移除和清理。IOptionsMonitorCache<TOptions>接口的默认实现是前面提到的OptionsCache<TOptions>类型,OptionsManager对象会将其作为自身的“私有”缓存。实现在OptionsCache<TOptions>类型中针对Options对象的缓存逻辑其实很简单:它仅仅使用一个ConcurrentDictionary<string, Lazy<TOptions>>对象作为缓存Options的容器而已。如下所示的代码片段基本上体现了OptionsCache<TOptions>类型的实现逻辑。

public class OptionsCache<TOptions> :  IOptionsMonitorCache<TOptions>
    where TOptions : class
{
    private readonly ConcurrentDictionary<string, Lazy<TOptions>> _cache =   new ConcurrentDictionary<string, Lazy<TOptions>>(StringComparer.Ordinal);
    public void Clear() => _cache.Clear();
    public virtual TOptions GetOrAdd(string name, Func<TOptions> createOptions)   => _cache.GetOrAdd(name, new Lazy<TOptions>(createOptions)).Value;
    public virtual bool TryAdd(string name, TOptions options)  => _cache.TryAdd(name, new Lazy<TOptions>(() => options));
    public virtual bool TryRemove(string name)   => _cache.TryRemove(name, out var ignored);
}

七、IOptionsMonitor<TOptions>

Options模型之所以将表示缓存的接口命名为IOptionsMonitorCache<TOptions>,是因为缓存最初是为IOptionsMonitor<TOptions>对象服务的,该对象旨在实现针对承载Options对象的原始数据源的监控,并在检测到数据更新后及时替换缓存的Options对象。

public interface IOptionsMonitor<out TOptions>
{
    TOptions CurrentValue { get; }
    TOptions Get(string name);
    IDisposable OnChange(Action<TOptions, string> listener);
}

除了直接调用定义在IOptionsMonitor<TOptions>接口中的OnChange方法注册应用新Options对象的回调,还可以调用如下这个同名的扩展方法。通过OnChange方法注册的回调是一个类型为Action<TOptions>的委托对象,由于缺少输出参数来区分Options的名称,所以注册的回调适用于所有的Options对象。值得一提的是,这两个OnChange方法的返回类型为IDisposable,实际上代表了针对回调的注册,我们可以调用返回对象的Dispose方法解除注册。

public static class OptionsMonitorExtensions
{
    public static IDisposable OnChange<TOptions>( this IOptionsMonitor<TOptions> monitor, Action<TOptions> listener)
        => monitor.OnChange((o, _) => listener(o));
}

.NET Core应用在进行数据变化监控时总是使用一个IChangeToken对象来发送通知,用于监控Options数据变化的IOptionsMonitor<TOptions>对象自然也不例外。IOptionsMonitor<TOptions>对象在检测到数据变化后用于对外发送通知的IChangeToken对象是由一个IOptionsChangeTokenSource<TOptions>对象完成的。IOptionsChangeTokenSource<TOptions>接口的Name属性表示Options的名称,而前面所说的IChangeToken对象由其GetChangeToken方法来提供。

public interface IOptionsChangeTokenSource<out TOptions>
{
    string Name { get; }
    IChangeToken GetChangeToken();
}

Options模型定义了如下这个OptionsMonitor<TOptions>类型作为对IOptionsMonitor<TOptions>接口的默认实现。当调用构造函数创建一个OptionsMonitor<TOptions>对象时需要提供一个用来创建和初始化Options对象的IOptionsFactory<TOptions>对象,一个用来对提供的Options对象实施缓存的IOptionsMonitorCache<TOptions>对象,以及一组用来检测配置选项数据变化并对外发送通知的IOptionsChangeTokenSource<TOptions>对象。

public class OptionsMonitor<TOptions> :IOptionsMonitor<TOptions> where TOptions : class, new()
{
    private readonly IOptionsMonitorCache<TOptions> _cache;
    private readonly IOptionsFactory<TOptions> _factory;
    private readonly IEnumerable<IOptionsChangeTokenSource<TOptions>> _sources;
    internal event Action<TOptions, string> _onChange;

    public OptionsMonitor(IOptionsFactory<TOptions> factory,IEnumerable<IOptionsChangeTokenSource<TOptions>> sources,IOptionsMonitorCache<TOptions> cache)
    {
        _factory = factory;
        _sources = sources;
        _cache = cache;

        foreach (var source in _sources)
        {
            ChangeToken.OnChange<string>(() => source.GetChangeToken(),(name) => InvokeChanged(name),source.Name);
        }
    }

    private void InvokeChanged(string name)
    {
        name = name ?? Options.DefaultName;
        _cache.TryRemove(name);
        var options = Get(name);
        if (_onChange != null)
        {
            _onChange.Invoke(options, name);
        }
    }

    public TOptions CurrentValue { get => Get(Options.DefaultName); }

    public virtual TOptions Get(string name) => _cache.GetOrAdd(name, () => _factory.Create(name));

    public IDisposable OnChange(Action<TOptions, string> listener)
    {
        var disposable = new ChangeTrackerDisposable(this, listener);
        _onChange += disposable.OnChange;
        return disposable;
    }

    internal class ChangeTrackerDisposable : IDisposable
    {
        private readonly Action<TOptions, string> _listener;
        private readonly OptionsMonitor<TOptions> _monitor;

        public ChangeTrackerDisposable(OptionsMonitor<TOptions> monitor, Action<TOptions, string> listener)
        {
            _listener = listener;
            _monitor = monitor;
        }

        public void OnChange(TOptions options, string name) => _listener.Invoke(options, name);
        public void Dispose() => _monitor._onChange -= OnChange;
    }
}

由于OptionsMonitor<TOptions>对象提供的Options对象总是来源于IOptionsMonitorCache<TOptions>对象表示的缓存容器,所以它只需要利用提供的IOptionsChangeTokenSource对象来监控Options数据的变化,并在检测到变化之后及时删除缓存中对应的Options对象,这样就能保证其CurrentValue属性和Get方法返回的总是最新的Options数据,这样的逻辑反映在上面给出的代码片段中。

[ASP.NET Core 3框架揭秘] Options[1]: 配置选项的正确使用方式[上篇]
[ASP.NET Core 3框架揭秘] Options[2]: 配置选项的正确使用方式[下篇]
[ASP.NET Core 3框架揭秘] Options[3]: Options模型[上篇]
[ASP.NET Core 3框架揭秘] Options[4]: Options模型[下篇]
[ASP.NET Core 3框架揭秘] Options[5]: 依赖注入
[ASP.NET Core 3框架揭秘] Options[6]: 扩展与定制
[ASP.NET Core 3框架揭秘] Options[7]: 与配置系统的整合

原文地址:https://www.cnblogs.com/artech/p/inside-asp-net-core-06-04.html

时间: 2024-07-30 03:13:24

[ASP.NET Core 3框架揭秘] Options[4]: Options模型[下篇]的相关文章

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [上篇]

微软在千禧年推出 .NET战略,并在两年后推出第一个版本的.NET Framework和IDE(Visual Studio.NET 2002,后来改名为Visual Studio),如果你是一个资深的.NET程序员,相信传统的.NET应用的开发方式已经深深地烙印在你的脑子里面..NET Core带来了全新的开发体验,但开发方式的差异根本不足以成为你快速跨入.NET Core 世界的门槛,因为在.NET Core在很多方面比传统的.NET Framework应用开发要简单.为了消除很多尚未接触过.

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Linux

如果想体验Linux环境下开发.NET Core应用,我们有多种选择.一种就是在一台物理机上安装原生的Linux,我们可以根据自身的喜好选择某种Linux Distribution,目前来说像RHEL.Ubuntu.Debian.Fedora.CentOS和SUSE这些主流的Distribution都是支持的.如果读者朋友们觉得这种方式比较麻烦,我们也可以采用虚拟机的形式安装相应的Linux Distribution,比如我经常使用的都是安装在VirtualBox上的Ubuntu.对于X64 W

[ASP.NET Core 3框架揭秘] Options[7]: 与配置系统的整合

Options模型本身与配置系统完全没有关系,但是配置在大部分情况下会作为绑定Options对象的数据源,所以有必要将两者结合在一起.与<扩展与定制>演示的两个例子一样,针对配置系统的集成同样是通过定制Options模型相应的对象来实现的.具体来说,集成配置系统需要解决如下两个问题: 将承载配置数据的IConfiguration对象绑定为Options对象. 自动感知配置数据的变化. 第一个问题涉及针对Options对象的初始化问题,这自然是通过自定义IConfigureOptions<

[ASP.NET Core 3框架揭秘] 依赖注入[5]: 利用容器提供服务

毫不夸张地说,整个ASP.NET Core框架是建立在依赖注入框架之上的.ASP.NET Core应用在启动时构建管道以及利用该管道处理每个请求过程中使用到的服务对象均来源于依赖注入容器.该依赖注入容器不仅为ASP.NET Core框架自身提供必要的服务,同时也是应用程序的服务提供者,依赖注入已经成为了ASP.NET Core应用的基本编程模式. 一.服务的注册与消费 为了让读者朋友们能够更加容易地认识.NET Core提供的依赖注入框架,我在"<一个迷你版DI框架>"中特

[ASP.NET Core 3框架揭秘] 配置[6]:多样化的配置源[上篇]

.NET Core采用的这个全新的配置模型的一个主要的特点就是对多种不同配置源的支持.我们可以将内存变量.命令行参数.环境变量和物理文件作为原始配置数据的来源.如果采用物理文件作为配置源,我们可以选择不同的格式(比如XML.JSON和INI等).如果这些默认支持的配置源形式还不能满足你的需求,我们还可以通过注册自定义IConfigurationSource的方式将其他形式数据作为配置来源. 一.MemoryConfigurationSource 在之前的实例演示都在使用MemoryConfigu

[ASP.NET Core 3框架揭秘] 配置[9]:自定义配置源

我们在前面对配置模型中默认提供的各种IConfigurationSource实现类型进行了深入详尽的介绍,如果它们依然不能满足项目中的需求,我们还可以通过自定义IConfigurationSource实现类型来支持我们希望的配置源.就配置数据的持久化方式来说,将配置存储在数据库中应该是一种常见的方式.接下来我们会创建一个针对数据库的IConfigurationSource实现类型,它采用Entity Framework Core来完成数据库的存取操作. 我们将这个自定义Configuration

[ASP.NET Core 3框架揭秘] 依赖注入:控制反转

ASP.NET Core框架建立在一些核心的基础框架之上,这些基础框架包括依赖注入.文件系统.配置选项和诊断日志等.这些框架不仅仅是支撑ASP.NET Core框架的基础,我们在进行应用开发的时候同样会频繁地使用到它们.对于这里提到的这几个基础框架,依赖注入尤为重要.ASP.NET Core应用在启动以及后续针对请求的处理过程中,它会依赖各种的组件提供服务.为了便于定制,这些组件一般会以接口的形式进行"标准化",我们将这些标准化的组件统一称为"服务(Service)"

[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

原文:[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式 正如我们在<依赖注入:控制反转>提到过的,很多人将IoC理解为一种"面向对象的设计模式",实际上IoC不仅与面向对象没有必然的联系,它自身甚至算不上是一种设计模式.一般来讲,设计模式提供了一种解决某种具体问题的方案,但是IoC既没有一个针对性的问题领域,其自身也没有提供一种可操作性的解决方案,所以我们更加倾向于将IoC视为一种设计原则.很多我们熟悉的设计模式背后都采用了IoC原则,接下来我们就来介绍几种典

[ASP.NET Core 3框架揭秘] 依赖注入:依赖注入模式

原文:[ASP.NET Core 3框架揭秘] 依赖注入:依赖注入模式 IoC主要体现了这样一种设计思想:通过将一组通用流程的控制权从应用转移到框架之中以实现对流程的复用,并按照"好莱坞法则"实现应用程序的代码与框架之间的交互.我们可以采用若干设计模式以不同的方式实现IoC,比如我们在前面介绍的模板方法.工厂方法和抽象工厂,接下来我们介绍一种更有价值的IoC模式:依赖注入(DI:Dependency Injection). 一.由容器提供对象 和前面介绍的工厂方法和抽象工厂模式一样,依