Add extension methods to auto-reload a configuration on file change
This commit is contained in:
Родитель
854b5d8610
Коммит
d28a36bc84
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 14
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 14.0.23107.0
|
VisualStudioVersion = 14.0.23107.0
|
||||||
|
@ -45,6 +44,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Config
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Configuration.FileExtensions.Test", "test\Microsoft.Extensions.Configuration.FileExtensions.Test\Microsoft.Extensions.Configuration.FileExtensions.Test.xproj", "{F7932F19-EB68-4C52-9CD1-3B51E48C2337}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Configuration.FileExtensions.Test", "test\Microsoft.Extensions.Configuration.FileExtensions.Test\Microsoft.Extensions.Configuration.FileExtensions.Test.xproj", "{F7932F19-EB68-4C52-9CD1-3B51E48C2337}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Configuration.FileProviderExtensions", "src\Microsoft.Extensions.Configuration.FileProviderExtensions\Microsoft.Extensions.Configuration.FileProviderExtensions.xproj", "{5BCFD3A8-B031-4831-9BC4-4A1733F63059}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Configuration.FileProviderExtensions.Test", "test\Microsoft.Extensions.Configuration.FileProviderExtensions.Test\Microsoft.Extensions.Configuration.FileProviderExtensions.Test.xproj", "{CC96A8D9-7524-4658-8653-8CAB9AD5839C}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -269,6 +272,30 @@ Global
|
||||||
{F7932F19-EB68-4C52-9CD1-3B51E48C2337}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{F7932F19-EB68-4C52-9CD1-3B51E48C2337}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{F7932F19-EB68-4C52-9CD1-3B51E48C2337}.Release|x86.ActiveCfg = Release|Any CPU
|
{F7932F19-EB68-4C52-9CD1-3B51E48C2337}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{F7932F19-EB68-4C52-9CD1-3B51E48C2337}.Release|x86.Build.0 = Release|Any CPU
|
{F7932F19-EB68-4C52-9CD1-3B51E48C2337}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -293,5 +320,7 @@ Global
|
||||||
{AE6FFE9B-6378-4D57-AA24-7D257F18B235} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF}
|
{AE6FFE9B-6378-4D57-AA24-7D257F18B235} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF}
|
||||||
{881E7CBC-492C-47C5-98A6-61DD1C753EE6} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1}
|
{881E7CBC-492C-47C5-98A6-61DD1C753EE6} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1}
|
||||||
{F7932F19-EB68-4C52-9CD1-3B51E48C2337} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF}
|
{F7932F19-EB68-4C52-9CD1-3B51E48C2337} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF}
|
||||||
|
{5BCFD3A8-B031-4831-9BC4-4A1733F63059} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1}
|
||||||
|
{CC96A8D9-7524-4658-8653-8CAB9AD5839C} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Microsoft.Extensions.Configuration
|
namespace Microsoft.Extensions.Configuration
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNet.FileProviders;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Configuration
|
||||||
|
{
|
||||||
|
public static class FileProviderExtensions
|
||||||
|
{
|
||||||
|
public static IConfigurationRoot ReloadOnChanged(this IConfigurationRoot config, string filename)
|
||||||
|
{
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(filename));
|
||||||
|
}
|
||||||
|
#if NET451
|
||||||
|
var basePath = AppDomain.CurrentDomain.GetData("APP_CONTEXT_BASE_DIRECTORY") as string ??
|
||||||
|
AppDomain.CurrentDomain.BaseDirectory ?? string.Empty;
|
||||||
|
#else
|
||||||
|
var basePath = AppContext.BaseDirectory ?? string.Empty;
|
||||||
|
#endif
|
||||||
|
return ReloadOnChanged(config, basePath, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IConfigurationRoot ReloadOnChanged(
|
||||||
|
this IConfigurationRoot config,
|
||||||
|
string basePath,
|
||||||
|
string filename)
|
||||||
|
{
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (basePath == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(basePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileProvider = new PhysicalFileProvider(basePath);
|
||||||
|
return ReloadOnChanged(config, fileProvider, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IConfigurationRoot ReloadOnChanged(
|
||||||
|
this IConfigurationRoot config,
|
||||||
|
IFileProvider fileProvider,
|
||||||
|
string filename)
|
||||||
|
{
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(fileProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
Action<object> callback = null;
|
||||||
|
callback = _ =>
|
||||||
|
{
|
||||||
|
// The order here is important. We need to take the token and then apply our changes BEFORE
|
||||||
|
// registering. This prevents us from possible having two change updates to process concurrently.
|
||||||
|
//
|
||||||
|
// If the file changes after we take the token, then we'll process the update immediately upon
|
||||||
|
// registering the callback.
|
||||||
|
var token = fileProvider.Watch(filename);
|
||||||
|
config.Reload();
|
||||||
|
token.RegisterChangeCallback(callback, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
fileProvider.Watch(filename).RegisterChangeCallback(callback, null);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>5bcfd3a8-b031-4831-9bc4-4a1733f63059</ProjectGuid>
|
||||||
|
<RootNamespace>Microsoft.Extensions.Configuration.FileProviderExtensions</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"description": "Extension methods for using configuration with file providers.",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/aspnet/configuration"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.FileProviders.Physical": "1.0.0-*",
|
||||||
|
"Microsoft.Extensions.Configuration.Abstractions": "1.0.0-*",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-*"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net451": { },
|
||||||
|
"dotnet5.4": { },
|
||||||
|
"netcore50": { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using Microsoft.AspNet.FileProviders;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Configuration
|
||||||
|
{
|
||||||
|
public class ConfigurationRootExtensionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ReloadOnChanged_GetTokenBeforeReload()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var tokenSource1 = new CancellationTokenSource();
|
||||||
|
var tokenSource2 = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var fileProvider = new MockFileProvider();
|
||||||
|
fileProvider.Cancel = tokenSource1;
|
||||||
|
|
||||||
|
var configuration = new MockConfigurationRoot();
|
||||||
|
configuration.OnReload = () => Assert.Equal(2, fileProvider.WatchCount);
|
||||||
|
|
||||||
|
// Act-1
|
||||||
|
configuration.ReloadOnChanged(fileProvider, "config.json");
|
||||||
|
|
||||||
|
// Assert-1
|
||||||
|
Assert.Equal(1, fileProvider.WatchCount);
|
||||||
|
Assert.Equal(0, configuration.ReloadCount);
|
||||||
|
|
||||||
|
// Act-2
|
||||||
|
fileProvider.Cancel = tokenSource2;
|
||||||
|
tokenSource1.Cancel();
|
||||||
|
|
||||||
|
Assert.Equal(2, fileProvider.WatchCount);
|
||||||
|
Assert.Equal(1, configuration.ReloadCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockConfigurationRoot : IConfigurationRoot
|
||||||
|
{
|
||||||
|
public Action OnReload { get; set; }
|
||||||
|
|
||||||
|
public int ReloadCount { get; private set; }
|
||||||
|
|
||||||
|
public string this[string key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IConfigurationSection> GetChildren()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IChangeToken GetReloadToken()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfigurationSection GetSection(string key)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reload()
|
||||||
|
{
|
||||||
|
OnReload?.Invoke();
|
||||||
|
ReloadCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockFileProvider : IFileProvider
|
||||||
|
{
|
||||||
|
public CancellationTokenSource Cancel { get; set; }
|
||||||
|
|
||||||
|
public int WatchCount { get; private set; }
|
||||||
|
|
||||||
|
public IDirectoryContents GetDirectoryContents(string subpath)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFileInfo GetFileInfo(string subpath)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IChangeToken Watch(string filter)
|
||||||
|
{
|
||||||
|
WatchCount++;
|
||||||
|
return new CancellationChangeToken(Cancel.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>cc96a8d9-7524-4658-8653-8cab9ad5839c</ProjectGuid>
|
||||||
|
<RootNamespace>Microsoft.Extensions.Configuration.FileProviderExtensions.Test</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-*",
|
||||||
|
"Microsoft.Extensions.Configuration.Test.Common": "1.0.0-*",
|
||||||
|
"xunit.runner.aspnet": "2.0.0-aspnet-*"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"dnx451": { },
|
||||||
|
"dnxcore50": { }
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"test": "xunit.runner.aspnet"
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче