Skip to content

Instantly share code, notes, and snippets.

@mr5z
Last active April 26, 2021 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mr5z/e7983ed550b3107857f89ca4e6bafc84 to your computer and use it in GitHub Desktop.
Save mr5z/e7983ed550b3107857f89ca4e6bafc84 to your computer and use it in GitHub Desktop.
Scheduled closing of config file
public class Configuration : IConfiguration
{
private readonly string assemblyResourceFile;
private AutoDisposeJsonDocument? smartJsonDocument;
public Configuration(string assemblyResourceFile)
{
this.assemblyResourceFile = assemblyResourceFile;
}
public static IConfiguration FromCurrentAssembly(string assemblyResourceFile)
{
var assemblyName = typeof(Configuration).Assembly.GetName().Name;
return new Configuration($"{assemblyName}.{assemblyResourceFile}");
}
private T? GetValueImpl<T>(string path, JsonDocument jsonDocument, JsonSerializerOptions? options)
{
var root = jsonDocument.RootElement;
var jsonPath = JsonPath.Parse(path);
var pathResult = jsonPath.Evaluate(root);
if (pathResult.Error != null)
throw new InvalidOperationException(pathResult.Error);
var matches = pathResult.Matches!;
var first = matches.First();
var value = typeof(T) == typeof(string) ?
$"\"{first.Value}\"" :
$"{first.Value}";
return JsonSerializer.Deserialize<T?>(value, options);
}
public T? GetValue<T>(string path, JsonSerializerOptions? options)
{
smartJsonDocument ??= new AutoDisposeJsonDocument(assemblyResourceFile);
var jsonDocument = smartJsonDocument.ParseJsonDocument();
return GetValueImpl<T>(path, jsonDocument, options);
}
public async Task<T?> GetValueAsync<T>(
string path, JsonSerializerOptions? options, CancellationToken cancellationToken)
{
smartJsonDocument ??= new AutoDisposeJsonDocument(assemblyResourceFile);
var jsonDocument = await smartJsonDocument.ParseJsonDocumentAsync(cancellationToken);
return GetValueImpl<T>(path, jsonDocument, options);
}
private protected class AutoDisposeJsonDocument
{
private readonly string jsonFile;
private Stream? jsonStream;
private JsonDocument? jsonDocument;
public AutoDisposeJsonDocument(string jsonFile)
{
this.jsonFile = jsonFile;
}
public JsonDocument ParseJsonDocument()
{
if (jsonDocument == null)
{
var stream = GetAutoDisposeStream();
jsonDocument = JsonDocument.Parse(stream);
}
return jsonDocument;
}
public async Task<JsonDocument> ParseJsonDocumentAsync(
CancellationToken cancellationToken)
{
if (jsonDocument == null)
{
var stream = GetAutoDisposeStream();
jsonDocument = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
}
return jsonDocument;
}
private Stream GetAutoDisposeStream()
{
if (jsonStream == null)
{
jsonStream = GetStream();
ScheduleDisposeResourceStreams();
}
return jsonStream;
}
private Stream GetStream()
{
var assembly = Assembly.GetExecutingAssembly();
var resourceNames = assembly.GetManifestResourceNames();
if (!resourceNames.Contains(jsonFile))
throw new FileNotFoundException($"Embedded resource not found: '{jsonFile}'");
return assembly.GetManifestResourceStream(jsonFile);
}
private void ScheduleDisposeResourceStreams()
{
_ = Task.Factory.StartNew(async () =>
{
await Task.Delay(TimeSpan.FromMinutes(1));
// in the same thread so no lock needed
jsonStream?.Dispose();
jsonDocument?.Dispose();
jsonStream = null;
jsonDocument = null;
}, default, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment