Last active
February 4, 2022 19:39
-
-
Save adams85/0b5192e81ff8eb89fc6d258c14a43761 to your computer and use it in GitHub Desktop.
Updateable JSON settings in ASP.NET Core 2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace AspNetCoreApp | |
{ | |
public class AdminSettings | |
{ | |
public AdminSettings() { } | |
// copy constructor | |
public AdminSettings(AdminSettings other) | |
{ | |
AdminEmail = other.AdminEmail; | |
} | |
public string AdminEmail { get; set; } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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