Asp. net core method example for implementing automatically updated Option

  • 2021-11-02 00:35:26
  • OfStack

Asp. net core can monitor the changes of json, xml and other configuration files, and automatically refresh the configuration contents in memory. However, if you want to get the latest configuration information from zookeeper and consul every 1 second, you need to implement it yourself.

After reading Asp. net core Document Custom configuration provider, I learned that I only need to implement my own IConfigurationSource and corresponding ConfigurationProvider

In this example, I set up a simple option that contains only one ever-changing counter variable.


public class RefreshableOptions
{
  public int IncreasementCount { get; set; }
}

Realize IConfigurationSource and corresponding ConfigurationProvider. There is an timer simulation inside to get the latest data from outside. For the sake of simplicity, the path of option is specified by hard coding


public class AutoRefreshConfigurationSource : IConfigurationSource
{
  public IConfigurationProvider Build(IConfigurationBuilder builder)
  {
    return new AutoRefreshConfigurationProvider();
  }
}

public class AutoRefreshConfigurationProvider : ConfigurationProvider
{
  private int count = 0;
  private bool isChanged;

  public AutoRefreshConfigurationProvider() : base()
  {
    Timer timer = new Timer(TimerCallback);
    timer.Change(1000, 3000);
  }

  public override void Load()
  {
    var beforeData = Data;
    //  Hard-coded specification here option Path of 
    Data = new Dictionary<string, string>() { { "AutoRefreshOptions:IncreasementCount", count.ToString() } };
    isChanged = IsDictionaryChanged(beforeData, Data);
  }

  private void TimerCallback(object state)
  {
    count++;
    this.Load();
    if (isChanged)
    {
      base.OnReload();// Notice IConfiguration Instances ,  Some parameters have changed 
      isChanged = false;
    }
  }
  // Judge two Idictionary Are there different ways to help 
  private static bool IsDictionaryChanged(IDictionary<string, string> before, IDictionary<string, string> after)
  {
    if (before == null && after == null)
    {
      return false;
    }
    if ((before == null) != (after == null))
    {
      return true;
    }
    if (before.Count != after.Count)
    {
      return true;
    }
    var ignoreCaseBefore = new Dictionary<string, string>(before, StringComparer.OrdinalIgnoreCase);
    foreach (var afterItemKey in after.Keys)
    {
      if (!ignoreCaseBefore.TryGetValue(afterItemKey, out var beforeItemValue))
      {
        return true;
      }
      if (beforeItemValue != after[afterItemKey])
      {
        return true;
      }
      ignoreCaseBefore.Remove(afterItemKey);
    }
    if (ignoreCaseBefore.Count > 0)
    {
      return true;
    }
    return false;
  }
}

Implement the extension method


public static class AutoRereshConfigurationExtensions
{
  public static IConfigurationBuilder AddAutoRereshConfiguration(this IConfigurationBuilder builder)
  {
    return builder.Add(new AutoRefreshConfigurationSource());
  }
}

Usage

Create a new WebApi project and add yellow part in Program. CreateWebHostBuilder


WebHost.CreateDefaultBuilder(args)
  .ConfigureAppConfiguration(config =>
  {
    config.AddAutoRereshConfiguration();
  })
  .UseStartup<Startup>();

Configured in Startup. ConfigureServices


services.Configure<RefreshableOptions>(Configuration.GetSection("AutoRefreshOptions"));

Modify ValuesController


[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
  private RefreshableOptions refreshableOptions;
  public ValuesController(IOptionsSnapshot<RefreshableOptions> refreshableOptions)
  {
    this.refreshableOptions = refreshableOptions.Value;
  }

  [HttpGet]
  public ActionResult<IEnumerable<string>> Get()
  {
    return new string[] { "value1", "value2", refreshableOptions.IncreasementCount.ToString() };
  }
}

Refresh http://localhost: 5000/api/values after startup to see the change of returned content

Text code


Related articles: