Skip to content

Instantly share code, notes, and snippets.

@adams85
Last active February 4, 2022 19:39
Show Gist options
  • Save adams85/0b5192e81ff8eb89fc6d258c14a43761 to your computer and use it in GitHub Desktop.
Save adams85/0b5192e81ff8eb89fc6d258c14a43761 to your computer and use it in GitHub Desktop.
Updateable JSON settings in ASP.NET Core 2
namespace AspNetCoreApp
{
public class AdminSettings
{
public AdminSettings() { }
// copy constructor
public AdminSettings(AdminSettings other)
{
AdminEmail = other.AdminEmail;
}
public string AdminEmail { get; set; }
}
}
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
namespace AspNetCoreApp
{
class AdminSettingsConfigurationProvider : JsonConfigurationProvider
{
public AdminSettingsConfigurationProvider(JsonConfigurationSource source) : base(source) { }
public void Reload()
{
// based on https://github.com/aspnet/Configuration/blob/2.1.1/src/Config.FileExtensions/FileConfigurationProvider.cs#L46
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var file = Source.FileProvider?.GetFileInfo(Source.Path);
if (file != null && file.Exists)
using (var stream = file.CreateReadStream())
Load(stream);
OnReload();
}
}
class AdminSettingsConfigurationSource : JsonConfigurationSource
{
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
Optional = true;
ReloadOnChange = false; // we manually signal the changes, don't want to rely on file system notifications
EnsureDefaults(builder);
return new AdminSettingsConfigurationProvider(this);
}
}
}
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace AspNetCoreApp
{
public static class AdminSettingsExtensions
{
public static IWebHostBuilder UseAdminSettings(this IWebHostBuilder @this, string path)
{
return @this
.ConfigureServices((ctx, services) =>
{
var configuration = new ConfigurationBuilder()
.SetBasePath(ctx.HostingEnvironment.ContentRootPath)
.Add(new AdminSettingsConfigurationSource { Path = path })
.Build();
var configurationProvider = (AdminSettingsConfigurationProvider)configuration.Providers.Single();
services.AddSingleton<IAdminSettingsManager>(sp => new AdminSettingsManager(configurationProvider, sp.GetRequiredService<IOptionsMonitor<AdminSettings>>()));
services.Configure<AdminSettings>(configuration);
});
}
}
}
using System;
using System.IO;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace AspNetCoreApp
{
public interface IAdminSettingsManager
{
AdminSettings Current { get; }
void Update(Action<AdminSettings> change);
}
class AdminSettingsManager : IAdminSettingsManager
{
readonly object syncLock = new object();
readonly AdminSettingsConfigurationProvider _configurationProvider;
readonly IOptionsMonitor<AdminSettings> _optionsMonitor;
public AdminSettingsManager(AdminSettingsConfigurationProvider configurationProvider, IOptionsMonitor<AdminSettings> optionsMonitor)
{
_configurationProvider = configurationProvider;
_optionsMonitor = optionsMonitor;
}
public AdminSettings Current => _optionsMonitor.CurrentValue;
public void Update(Action<AdminSettings> configure)
{
lock (syncLock)
{
var newOptions = new AdminSettings(Current);
configure(newOptions);
// writing new settings (with some fault tolerance)
var configSource = _configurationProvider.Source;
var fileName = Path.Combine(((PhysicalFileProvider)configSource.FileProvider).Root, configSource.Path);
var tempFileName = fileName + ".new";
File.WriteAllText(tempFileName, JsonConvert.SerializeObject(newOptions));
if (File.Exists(fileName))
File.Delete(fileName);
File.Move(tempFileName, fileName);
// signalling change
_configurationProvider.Reload();
}
}
}
}
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace AspNetCoreApp
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseAdminSettings(@"App_Data\adminsettings.json")
.UseStartup<Startup>();
}
}
using Microsoft.AspNetCore.Mvc;
namespace AspNetCoreApp.Controllers
{
public class SampleController : Controller
{
readonly IAdminSettingsManager _adminSettingsManager;
// you can inject IOptionsMonitor<AdminSettings> if you want to just read the settings
public SampleController(IAdminSettingsManager adminSettingsManager)
{
_adminSettingsManager = adminSettingsManager;
}
public IActionResult Index()
{
// retrieve settings
var settings = _adminSettingsManager.Current;
return View(settings);
}
public IActionResult Update()
{
// update settings
_adminSettingsManager.Update(settings => settings.AdminEmail = "[email protected]");
return RedirectToAction(nameof(Index));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment