<sub id="gqw76"><listing id="gqw76"></listing></sub>
      <sub id="gqw76"><listing id="gqw76"></listing></sub>

    1. <form id="gqw76"><legend id="gqw76"></legend></form>
    2. 重新整理 .net core 實踐篇—————服務的配置更新[十三]

      前言

      前文講述了,服務和配置直接的配合,這一節寫一下,當配置文件修改了,每個服務如何感知自己的配置。

      正文

      服務感知到自己的配置發生變化,這就牽扯出兩個東西:

      IoptionsMonitor<out TOptions>
      
      IoptionSnapshot<out TOptions>
      

      在作用域范圍使用IoptionSnapshot,在單例中使用IoptionsMonitor 。

      IoptionsMonitor

      先來演示作用域范圍的使用。
      配置:

      {
        "SelfService": {
          "name" : "zhangsan" 
        }
      }
      

      SelfServiceOption:

      public class SelfServiceOption
      {
      	public string Name { get; set; }
      }
      

      服務:

      public class SelfService : ISelfService
      {
      	IOptionsSnapshot<SelfServiceOption> _options;
      	public SelfService(IOptionsSnapshot<SelfServiceOption> options)
      	{
      		this._options = options;
      	}
      	public string ShowOptionName()
      	{
      		return _options.Value.Name;
      	}
      }
      

      注冊:

      services.Configure<SelfServiceOption>(Configuration.GetSection("SelfService"), BinderOptions =>
      {
      	BinderOptions.BindNonPublicProperties = true;
      });
      services.AddScoped<ISelfService, SelfService>();
      

      測試:

      [HttpGet]
      public int GetService([FromServices]ISelfService selfService)
      {
      	Console.WriteLine(selfService.ShowOptionName());
      	return 1;
      }
      

      結果:

      第一次訪問后修改為zhangsan_change,再次訪問接口,會呈現上述效果。

      那么為什么使用IoptionsMonitor,而為什么Ioptions 沒有用呢。

      前一篇寫過Ioptions 的實現類OptionsManager,這個是有緩存的_cache,如下:

      public class OptionsManager<TOptions> : IOptions<TOptions>, IOptionsSnapshot<TOptions> where TOptions : class, new()
      {
      	private readonly IOptionsFactory<TOptions> _factory;
      	private readonly OptionsCache<TOptions> _cache = new OptionsCache<TOptions>(); // Note: this is a private cache
      
      	/// <summary>
      	/// Initializes a new instance with the specified options configurations.
      	/// </summary>
      	/// <param name="factory">The factory to use to create options.</param>
      	public OptionsManager(IOptionsFactory<TOptions> factory)
      	{
      		_factory = factory;
      	}
      
      	/// <summary>
      	/// The default configured <typeparamref name="TOptions"/> instance, equivalent to Get(Options.DefaultName).
      	/// </summary>
      	public TOptions Value
      	{
      		get
      		{
      			return Get(Options.DefaultName);
      		}
      	}
      
      	/// <summary>
      	/// Returns a configured <typeparamref name="TOptions"/> instance with the given <paramref name="name"/>.
      	/// </summary>
      	public virtual TOptions Get(string name)
      	{
      		name = name ?? Options.DefaultName;
      
      		// Store the options in our instance cache
      		return _cache.GetOrAdd(name, () => _factory.Create(name));
      	}
      }
      

      IoptionsMonitor的實現類也是OptionsManager,但是人家是作用域模式。

      在Addoptions中:

      services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
      services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));
      

      也就是說每創建一個SelfService,就會創建一個OptionsManager。緩存自然只在作用域內有效。

      好的,那么來看下單例。

      IoptionsMonitor

      服務:

      public class SelfService : ISelfService
      {
      	IOptionsMonitor<SelfServiceOption> _options;
      	public SelfService(IOptionsMonitor<SelfServiceOption> options)
      	{
      		this._options = options;
      
      		_options.OnChange((selftServiceOptions) =>
      		{
      			Console.WriteLine("alter change:"+selftServiceOptions.Name);
      		});
      	}
      	public string ShowOptionName()
      	{
      		return _options.CurrentValue.Name;
      	}
      }
      

      注冊:

      services.Configure<SelfServiceOption>(Configuration.GetSection("SelfService"), BinderOptions =>
      {
      	BinderOptions.BindNonPublicProperties = true;
      });
      services.AddSingleton<ISelfService, SelfService>();
      

      測試接口:

      [HttpGet]
      public int GetService([FromServices]ISelfService selfService)
      {
      	Console.WriteLine(selfService.ShowOptionName());
      	return 1;
      }
      

      同意是修改錢訪問一次,修改后訪問一次。

      結果如下:

      那么看下IOptionMonitor的實現類OptionsMonitor:

      public class OptionsMonitor<TOptions> : IOptionsMonitor<TOptions>, IDisposable where TOptions : class, new()
      {
      	private readonly IOptionsMonitorCache<TOptions> _cache;
      	private readonly IOptionsFactory<TOptions> _factory;
      	private readonly IEnumerable<IOptionsChangeTokenSource<TOptions>> _sources;
      	private readonly List<IDisposable> _registrations = new List<IDisposable>();
      	internal event Action<TOptions, string> _onChange;
      
      	/// <summary>
      	/// Constructor.
      	/// </summary>
      	/// <param name="factory">The factory to use to create options.</param>
      	/// <param name="sources">The sources used to listen for changes to the options instance.</param>
      	/// <param name="cache">The cache used to store options.</param>
      	public OptionsMonitor(IOptionsFactory<TOptions> factory, IEnumerable<IOptionsChangeTokenSource<TOptions>> sources, IOptionsMonitorCache<TOptions> cache)
      	{
      		_factory = factory;
      		_sources = sources;
      		_cache = cache;
      
      		foreach (var source in _sources)
      		{
      			var registration = ChangeToken.OnChange(
      				  () => source.GetChangeToken(),
      				  (name) => InvokeChanged(name),
      				  source.Name);
      
      			_registrations.Add(registration);
      		}
      	}
      
      	private void InvokeChanged(string name)
      	{
      		name = name ?? Options.DefaultName;
      		_cache.TryRemove(name);
      		var options = Get(name);
      		if (_onChange != null)
      		{
      			_onChange.Invoke(options, name);
      		}
      	}
      
      	/// <summary>
      	/// The present value of the options.
      	/// </summary>
      	public TOptions CurrentValue
      	{
      		get => Get(Options.DefaultName);
      	}
      
      	/// <summary>
      	/// Returns a configured <typeparamref name="TOptions"/> instance with the given <paramref name="name"/>.
      	/// </summary>
      	public virtual TOptions Get(string name)
      	{
      		name = name ?? Options.DefaultName;
      		return _cache.GetOrAdd(name, () => _factory.Create(name));
      	}
      
      	/// <summary>
      	/// Registers a listener to be called whenever <typeparamref name="TOptions"/> changes.
      	/// </summary>
      	/// <param name="listener">The action to be invoked when <typeparamref name="TOptions"/> has changed.</param>
      	/// <returns>An <see cref="IDisposable"/> which should be disposed to stop listening for changes.</returns>
      	public IDisposable OnChange(Action<TOptions, string> listener)
      	{
      		var disposable = new ChangeTrackerDisposable(this, listener);
      		_onChange += disposable.OnChange;
      		return disposable;
      	}
      
      	/// <summary>
      	/// Removes all change registration subscriptions.
      	/// </summary>
      	public void Dispose()
      	{
      		// Remove all subscriptions to the change tokens
      		foreach (var registration in _registrations)
      		{
      			registration.Dispose();
      		}
      
      		_registrations.Clear();
      	}
      
      	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;
      	}
      }
      

      給每個給做了監聽哈:

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

      這個IOptionsChangeTokenSource怎么來的呢?是在我們的configure配置方法中:

      public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, IConfiguration config, Action<BinderOptions> configureBinder)
      	where TOptions : class
      {
      	if (services == null)
      	{
      		throw new ArgumentNullException(nameof(services));
      	}
      
      	if (config == null)
      	{
      		throw new ArgumentNullException(nameof(config));
      	}
      
      	services.AddOptions();
      	services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, config));
      	return services.AddSingleton<IConfigureOptions<TOptions>>(new NamedConfigureFromConfigurationOptions<TOptions>(name, config, configureBinder));
      }
      

      看到這一段:services.AddSingleton<IOptionsChangeTokenSource>(new ConfigurationChangeTokenSource(name, config));。

      當有修改后,那么會調用:

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

      這里面會移除緩存_cache.TryRemove(name);,然后重新新調用: Get(name);也就會再綁定一次。

      這里面有一個值得注意的是,如果有回調,不一定是本身這個服務的配置修改,可能是其他服務的配置修改了,也會被通知,因為這個是文件發生變化就會被通知。

      原理如下:

      GetSession會返回一個 ConfigurationSection。那么它里面的GetReloadToken是這樣的:

       public IChangeToken GetReloadToken() => _root.GetReloadToken();
      

      這返回了ConfigurationRoot的GetReloadToken。

      實驗一下:

      {
          "SelfService": {
          "name": "zhangsan"
        },
        "SelfService2": {
          "name" : "good one" 
        }
      }
      

      改成:

      {
          "SelfService": {
          "name": "zhangsan"
        },
        "SelfService2": {
          "name" : "good one1" 
        }
      }
      

      結果:

      索引我們可以在服務里面配置增加一個version版本號,如果版本修改了,然后才做相應的操作。

      以上只是個人整理,如有錯誤,望請指點。

      下一節:配置驗證。

      posted @ 2021-06-07 06:47  敖毛毛  閱讀(568)  評論(1編輯  收藏  舉報
      最新chease0ldman老人|无码亚洲人妻下载|大香蕉在线看好吊妞视频这里有精品www|亚洲色情综合网

        <sub id="gqw76"><listing id="gqw76"></listing></sub>
        <sub id="gqw76"><listing id="gqw76"></listing></sub>

      1. <form id="gqw76"><legend id="gqw76"></legend></form>