Merge branch 'release' into dev

This commit is contained in:
Hao Kung 2016-04-26 16:40:35 -07:00
Родитель 566111d1e5 7e98d3c1dc
Коммит c634cacf4e
6 изменённых файлов: 92 добавлений и 226 удалений

Просмотреть файл

@ -1,94 +0,0 @@
// 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.IO;
using Microsoft.Extensions.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;
}
}
}

Просмотреть файл

@ -57,6 +57,35 @@ namespace Microsoft.Extensions.Configuration
});
}
/// <summary>
/// Adds the INI configuration provider at <paramref name="path"/> to <paramref name="builder"/>.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="path">Path relative to the base path stored in
/// <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
/// <param name="optional">Whether the file is optional.</param>
/// <param name="reloadOnChange">Whether the configuration should be reloaded if the file changes.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder AddIniFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path));
}
return AddIniFile(builder, source =>
{
source.Path = path;
source.Optional = optional;
source.ReloadOnChange = reloadOnChange;
});
}
/// <summary>
/// Adds a INI configuration source to <paramref name="builder"/>.
/// </summary>

Просмотреть файл

@ -60,6 +60,35 @@ namespace Microsoft.Extensions.Configuration
});
}
/// <summary>
/// Adds the JSON configuration provider at <paramref name="path"/> to <paramref name="builder"/>.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="path">Path relative to the base path stored in
/// <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
/// <param name="optional">Whether the file is optional.</param>
/// <param name="reloadOnChange">Whether the configuration should be reloaded if the file changes.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path));
}
return AddJsonFile(builder, source =>
{
source.Path = path;
source.Optional = optional;
source.ReloadOnChange = reloadOnChange;
});
}
/// <summary>
/// Adds a JSON configuration source to <paramref name="builder"/>.
/// </summary>

Просмотреть файл

@ -60,6 +60,35 @@ namespace Microsoft.Extensions.Configuration
});
}
/// <summary>
/// Adds the XML configuration provider at <paramref name="path"/> to <paramref name="builder"/>.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="path">Path relative to the base path stored in
/// <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
/// <param name="optional">Whether the file is optional.</param>
/// <param name="reloadOnChange">Whether the configuration should be reloaded if the file changes.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder AddXmlFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path));
}
return AddXmlFile(builder, source =>
{
source.Path = path;
source.Optional = optional;
source.ReloadOnChange = reloadOnChange;
});
}
/// <summary>
/// Adds a XML configuration source to <paramref name="builder"/>.
/// </summary>

Просмотреть файл

@ -1,117 +0,0 @@
// 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.Extensions.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++;
}
public void RaiseChanged()
{
throw new NotImplementedException();
}
public IDisposable RegisterOnChange(Action<object> callback, object state)
{
throw new NotImplementedException();
}
}
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);
}
}
}
}

Просмотреть файл

@ -251,21 +251,11 @@ CommonKey3:CommonKey4=IniValue6";
File.WriteAllText(Path.Combine(_basePath, "reload.ini"), @"IniKey1 = IniValue1");
File.WriteAllText(Path.Combine(_basePath, "reload.xml"), @"<settings XmlKey1=""XmlValue1""/>");
var config = new ConfigurationBuilder().AddIniFile(source =>
{
source.Path = "reload.ini";
source.ReloadOnChange = true;
})
.AddJsonFile(source =>
{
source.Path = "reload.json";
source.ReloadOnChange = true;
})
.AddXmlFile(source =>
{
source.Path = "reload.xml";
source.ReloadOnChange = true;
}).Build();
var config = new ConfigurationBuilder()
.AddIniFile("reload.ini", optional: false, reloadOnChange: true)
.AddJsonFile("reload.json", optional: false, reloadOnChange: true)
.AddXmlFile("reload.xml", optional: false, reloadOnChange: true)
.Build();
Assert.Equal("JsonValue1", config["JsonKey1"]);
Assert.Equal("IniValue1", config["IniKey1"]);