Add ReloadOnChange to KeyPerFile configuration provider (dotnet/extensions#2808)
\n\nCommit migrated from cca1c7ca95
This commit is contained in:
parent
44c226ccac
commit
4aab03bf9a
|
|
@ -6,14 +6,17 @@ namespace Microsoft.Extensions.Configuration
|
||||||
public static partial class KeyPerFileConfigurationBuilderExtensions
|
public static partial class KeyPerFileConfigurationBuilderExtensions
|
||||||
{
|
{
|
||||||
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action<Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource> configureSource) { throw null; }
|
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action<Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource> configureSource) { throw null; }
|
||||||
|
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath) { throw null; }
|
||||||
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; }
|
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; }
|
||||||
|
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional, bool reloadOnChange) { throw null; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace Microsoft.Extensions.Configuration.KeyPerFile
|
namespace Microsoft.Extensions.Configuration.KeyPerFile
|
||||||
{
|
{
|
||||||
public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider
|
public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider, System.IDisposable
|
||||||
{
|
{
|
||||||
public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { }
|
public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { }
|
||||||
|
public void Dispose() { }
|
||||||
public override void Load() { }
|
public override void Load() { }
|
||||||
public override string ToString() { throw null; }
|
public override string ToString() { throw null; }
|
||||||
}
|
}
|
||||||
|
|
@ -24,6 +27,8 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile
|
||||||
public System.Func<string, bool> IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
public System.Func<string, bool> IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||||
public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||||
public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||||
|
public int ReloadDelay { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||||
|
public bool ReloadOnChange { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||||
public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; }
|
public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,17 @@ namespace Microsoft.Extensions.Configuration
|
||||||
public static partial class KeyPerFileConfigurationBuilderExtensions
|
public static partial class KeyPerFileConfigurationBuilderExtensions
|
||||||
{
|
{
|
||||||
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action<Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource> configureSource) { throw null; }
|
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action<Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource> configureSource) { throw null; }
|
||||||
|
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath) { throw null; }
|
||||||
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; }
|
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; }
|
||||||
|
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional, bool reloadOnChange) { throw null; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace Microsoft.Extensions.Configuration.KeyPerFile
|
namespace Microsoft.Extensions.Configuration.KeyPerFile
|
||||||
{
|
{
|
||||||
public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider
|
public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider, System.IDisposable
|
||||||
{
|
{
|
||||||
public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { }
|
public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { }
|
||||||
|
public void Dispose() { }
|
||||||
public override void Load() { }
|
public override void Load() { }
|
||||||
public override string ToString() { throw null; }
|
public override string ToString() { throw null; }
|
||||||
}
|
}
|
||||||
|
|
@ -24,6 +27,8 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile
|
||||||
public System.Func<string, bool> IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
public System.Func<string, bool> IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||||
public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||||
public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||||
|
public int ReloadDelay { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||||
|
public bool ReloadOnChange { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||||
public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; }
|
public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,16 @@ namespace Microsoft.Extensions.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class KeyPerFileConfigurationBuilderExtensions
|
public static class KeyPerFileConfigurationBuilderExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds configuration using files from a directory. File names are used as the key,
|
||||||
|
/// file contents are used as the value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
|
||||||
|
/// <param name="directoryPath">The path to the directory.</param>
|
||||||
|
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
||||||
|
public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath)
|
||||||
|
=> builder.AddKeyPerFile(directoryPath, optional: false, reloadOnChange: false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds configuration using files from a directory. File names are used as the key,
|
/// Adds configuration using files from a directory. File names are used as the key,
|
||||||
/// file contents are used as the value.
|
/// file contents are used as the value.
|
||||||
|
|
@ -19,6 +29,18 @@ namespace Microsoft.Extensions.Configuration
|
||||||
/// <param name="optional">Whether the directory is optional.</param>
|
/// <param name="optional">Whether the directory is optional.</param>
|
||||||
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
||||||
public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath, bool optional)
|
public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath, bool optional)
|
||||||
|
=> builder.AddKeyPerFile(directoryPath, optional, reloadOnChange: false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds configuration using files from a directory. File names are used as the key,
|
||||||
|
/// file contents are used as the value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
|
||||||
|
/// <param name="directoryPath">The path to the directory.</param>
|
||||||
|
/// <param name="optional">Whether the directory is optional.</param>
|
||||||
|
/// <param name="reloadOnChange">Whether the configuration should be reloaded if the files are changed, added or removed.</param>
|
||||||
|
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
||||||
|
public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath, bool optional, bool reloadOnChange)
|
||||||
=> builder.AddKeyPerFile(source =>
|
=> builder.AddKeyPerFile(source =>
|
||||||
{
|
{
|
||||||
// Only try to set the file provider if its not optional or the directory exists
|
// Only try to set the file provider if its not optional or the directory exists
|
||||||
|
|
@ -27,6 +49,7 @@ namespace Microsoft.Extensions.Configuration
|
||||||
source.FileProvider = new PhysicalFileProvider(directoryPath);
|
source.FileProvider = new PhysicalFileProvider(directoryPath);
|
||||||
}
|
}
|
||||||
source.Optional = optional;
|
source.Optional = optional;
|
||||||
|
source.ReloadOnChange = reloadOnChange;
|
||||||
});
|
});
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.Configuration.KeyPerFile
|
namespace Microsoft.Extensions.Configuration.KeyPerFile
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A <see cref="ConfigurationProvider"/> that uses a directory's files as configuration key/values.
|
/// A <see cref="ConfigurationProvider"/> that uses a directory's files as configuration key/values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class KeyPerFileConfigurationProvider : ConfigurationProvider
|
public class KeyPerFileConfigurationProvider : ConfigurationProvider, IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly IDisposable _changeTokenRegistration;
|
||||||
|
|
||||||
KeyPerFileConfigurationSource Source { get; set; }
|
KeyPerFileConfigurationSource Source { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -16,7 +20,21 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The settings.</param>
|
/// <param name="source">The settings.</param>
|
||||||
public KeyPerFileConfigurationProvider(KeyPerFileConfigurationSource source)
|
public KeyPerFileConfigurationProvider(KeyPerFileConfigurationSource source)
|
||||||
=> Source = source ?? throw new ArgumentNullException(nameof(source));
|
{
|
||||||
|
Source = source ?? throw new ArgumentNullException(nameof(source));
|
||||||
|
|
||||||
|
if (Source.ReloadOnChange && Source.FileProvider != null)
|
||||||
|
{
|
||||||
|
_changeTokenRegistration = ChangeToken.OnChange(
|
||||||
|
() => Source.FileProvider.Watch("*"),
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
Thread.Sleep(Source.ReloadDelay);
|
||||||
|
Load(reload: true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static string NormalizeKey(string key)
|
private static string NormalizeKey(string key)
|
||||||
=> key.Replace("__", ConfigurationPath.KeyDelimiter);
|
=> key.Replace("__", ConfigurationPath.KeyDelimiter);
|
||||||
|
|
@ -27,15 +45,20 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile
|
||||||
: value;
|
: value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the docker secrets.
|
/// Loads the configuration values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override void Load()
|
public override void Load()
|
||||||
|
{
|
||||||
|
Load(reload: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Load(bool reload)
|
||||||
{
|
{
|
||||||
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
if (Source.FileProvider == null)
|
if (Source.FileProvider == null)
|
||||||
{
|
{
|
||||||
if (Source.Optional)
|
if (Source.Optional || reload) // Always optional on reload
|
||||||
{
|
{
|
||||||
Data = data;
|
Data = data;
|
||||||
return;
|
return;
|
||||||
|
|
@ -45,25 +68,32 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile
|
||||||
}
|
}
|
||||||
|
|
||||||
var directory = Source.FileProvider.GetDirectoryContents("/");
|
var directory = Source.FileProvider.GetDirectoryContents("/");
|
||||||
if (!directory.Exists && !Source.Optional)
|
if (!directory.Exists)
|
||||||
{
|
{
|
||||||
|
if (Source.Optional || reload) // Always optional on reload
|
||||||
|
{
|
||||||
|
Data = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
throw new DirectoryNotFoundException("The root directory for the FileProvider doesn't exist and is not optional.");
|
throw new DirectoryNotFoundException("The root directory for the FileProvider doesn't exist and is not optional.");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
foreach (var file in directory)
|
|
||||||
{
|
{
|
||||||
if (file.IsDirectory)
|
foreach (var file in directory)
|
||||||
{
|
{
|
||||||
continue;
|
if (file.IsDirectory)
|
||||||
}
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var stream = file.CreateReadStream();
|
||||||
|
using var streamReader = new StreamReader(stream);
|
||||||
|
|
||||||
using (var stream = file.CreateReadStream())
|
|
||||||
using (var streamReader = new StreamReader(stream))
|
|
||||||
{
|
|
||||||
if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name))
|
if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name))
|
||||||
{
|
{
|
||||||
data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd()));
|
data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,5 +109,11 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile
|
||||||
/// <returns> The configuration name. </returns>
|
/// <returns> The configuration name. </returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
=> $"{GetType().Name} for files in '{GetDirectoryName()}' ({(Source.Optional ? "Optional" : "Required")})";
|
=> $"{GetType().Name} for files in '{GetDirectoryName()}' ({(Source.Optional ? "Optional" : "Required")})";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_changeTokenRegistration?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,17 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Optional { get; set; }
|
public bool Optional { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the source will be loaded if the underlying file changes.
|
||||||
|
/// </summary>
|
||||||
|
public bool ReloadOnChange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of milliseconds that reload will wait before calling Load. This helps
|
||||||
|
/// avoid triggering reload before a file is completely written. Default is 250.
|
||||||
|
/// </summary>
|
||||||
|
public int ReloadDelay { get; set; } = 250;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds the <see cref="KeyPerFileConfigurationProvider"/> for this source.
|
/// Builds the <see cref="KeyPerFileConfigurationProvider"/> for this source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,79 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile.Test
|
||||||
Assert.Equal("Foo", options.Text);
|
Assert.Equal("Foo", options.Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReloadConfigWhenReloadOnChangeIsTrue()
|
||||||
|
{
|
||||||
|
var testFileProvider = new TestFileProvider(
|
||||||
|
new TestFile("Secret1", "SecretValue1"),
|
||||||
|
new TestFile("Secret2", "SecretValue2"));
|
||||||
|
|
||||||
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddKeyPerFile(o =>
|
||||||
|
{
|
||||||
|
o.FileProvider = testFileProvider;
|
||||||
|
o.ReloadOnChange = true;
|
||||||
|
}).Build();
|
||||||
|
|
||||||
|
Assert.Equal("SecretValue1", config["Secret1"]);
|
||||||
|
Assert.Equal("SecretValue2", config["Secret2"]);
|
||||||
|
|
||||||
|
testFileProvider.ChangeFiles(
|
||||||
|
new TestFile("Secret1", "NewSecretValue1"),
|
||||||
|
new TestFile("Secret3", "NewSecretValue3"));
|
||||||
|
|
||||||
|
Assert.Equal("NewSecretValue1", config["Secret1"]);
|
||||||
|
Assert.Null(config["NewSecret2"]);
|
||||||
|
Assert.Equal("NewSecretValue3", config["Secret3"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SameConfigWhenReloadOnChangeIsFalse()
|
||||||
|
{
|
||||||
|
var testFileProvider = new TestFileProvider(
|
||||||
|
new TestFile("Secret1", "SecretValue1"),
|
||||||
|
new TestFile("Secret2", "SecretValue2"));
|
||||||
|
|
||||||
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddKeyPerFile(o =>
|
||||||
|
{
|
||||||
|
o.FileProvider = testFileProvider;
|
||||||
|
o.ReloadOnChange = false;
|
||||||
|
}).Build();
|
||||||
|
|
||||||
|
Assert.Equal("SecretValue1", config["Secret1"]);
|
||||||
|
Assert.Equal("SecretValue2", config["Secret2"]);
|
||||||
|
|
||||||
|
testFileProvider.ChangeFiles(
|
||||||
|
new TestFile("Secret1", "NewSecretValue1"),
|
||||||
|
new TestFile("Secret3", "NewSecretValue3"));
|
||||||
|
|
||||||
|
Assert.Equal("SecretValue1", config["Secret1"]);
|
||||||
|
Assert.Equal("SecretValue2", config["Secret2"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NoFilesReloadWhenAddedFiles()
|
||||||
|
{
|
||||||
|
var testFileProvider = new TestFileProvider();
|
||||||
|
|
||||||
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddKeyPerFile(o =>
|
||||||
|
{
|
||||||
|
o.FileProvider = testFileProvider;
|
||||||
|
o.ReloadOnChange = true;
|
||||||
|
}).Build();
|
||||||
|
|
||||||
|
Assert.Empty(config.AsEnumerable());
|
||||||
|
|
||||||
|
testFileProvider.ChangeFiles(
|
||||||
|
new TestFile("Secret1", "SecretValue1"),
|
||||||
|
new TestFile("Secret2", "SecretValue2"));
|
||||||
|
|
||||||
|
Assert.Equal("SecretValue1", config["Secret1"]);
|
||||||
|
Assert.Equal("SecretValue2", config["Secret2"]);
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class MyOptions
|
private sealed class MyOptions
|
||||||
{
|
{
|
||||||
public int Number { get; set; }
|
public int Number { get; set; }
|
||||||
|
|
@ -227,17 +300,56 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile.Test
|
||||||
class TestFileProvider : IFileProvider
|
class TestFileProvider : IFileProvider
|
||||||
{
|
{
|
||||||
IDirectoryContents _contents;
|
IDirectoryContents _contents;
|
||||||
|
MockChangeToken _changeToken;
|
||||||
|
|
||||||
public TestFileProvider(params IFileInfo[] files)
|
public TestFileProvider(params IFileInfo[] files)
|
||||||
{
|
{
|
||||||
_contents = new TestDirectoryContents(files);
|
_contents = new TestDirectoryContents(files);
|
||||||
|
_changeToken = new MockChangeToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDirectoryContents GetDirectoryContents(string subpath) => _contents;
|
public IDirectoryContents GetDirectoryContents(string subpath) => _contents;
|
||||||
|
|
||||||
public IFileInfo GetFileInfo(string subpath) => new TestFile("TestDirectory");
|
public IFileInfo GetFileInfo(string subpath) => new TestFile("TestDirectory");
|
||||||
|
|
||||||
public IChangeToken Watch(string filter) => throw new NotImplementedException();
|
public IChangeToken Watch(string filter) => _changeToken;
|
||||||
|
|
||||||
|
internal void ChangeFiles(params IFileInfo[] files)
|
||||||
|
{
|
||||||
|
_contents = new TestDirectoryContents(files);
|
||||||
|
_changeToken.RaiseCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockChangeToken : IChangeToken
|
||||||
|
{
|
||||||
|
private Action _callback;
|
||||||
|
|
||||||
|
public bool ActiveChangeCallbacks => true;
|
||||||
|
|
||||||
|
public bool HasChanged => true;
|
||||||
|
|
||||||
|
public IDisposable RegisterChangeCallback(Action<object> callback, object state)
|
||||||
|
{
|
||||||
|
var disposable = new MockDisposable();
|
||||||
|
_callback = () => callback(state);
|
||||||
|
return disposable;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RaiseCallback()
|
||||||
|
{
|
||||||
|
_callback?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockDisposable : IDisposable
|
||||||
|
{
|
||||||
|
public bool Disposed { get; set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Disposed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestDirectoryContents : IDirectoryContents
|
class TestDirectoryContents : IDirectoryContents
|
||||||
|
|
@ -291,7 +403,7 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile.Test
|
||||||
|
|
||||||
public Stream CreateReadStream()
|
public Stream CreateReadStream()
|
||||||
{
|
{
|
||||||
if(IsDirectory)
|
if (IsDirectory)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Cannot create stream from directory");
|
throw new InvalidOperationException("Cannot create stream from directory");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue