Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Fix reload behaviors + Add OnLoadError event for file based config providers #497

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft.Extensions.Configuration
public static class FileConfigurationExtensions
{
private static string FileProviderKey = "FileProvider";
private static string FileLoadExceptionHandlerKey = "FileLoadExceptionHandler";

/// <summary>
/// Sets the default <see cref="IFileProvider"/> to be used for file-based providers.
Expand Down Expand Up @@ -54,8 +55,8 @@ public static IFileProvider GetFileProvider(this IConfigurationBuilder builder)
}

#if NET451
var stringBasePath = AppDomain.CurrentDomain.GetData("APP_CONTEXT_BASE_DIRECTORY") as string
?? AppDomain.CurrentDomain.BaseDirectory
var stringBasePath = AppDomain.CurrentDomain.GetData("APP_CONTEXT_BASE_DIRECTORY") as string
?? AppDomain.CurrentDomain.BaseDirectory
?? string.Empty;
return new PhysicalFileProvider(stringBasePath);
#else
Expand Down Expand Up @@ -83,5 +84,42 @@ public static IConfigurationBuilder SetBasePath(this IConfigurationBuilder build

return builder.SetFileProvider(new PhysicalFileProvider(basePath));
}

/// <summary>
/// Sets a default action to be invoked for file-based providers when an error occurs.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="handler">The Action to be invoked on a file load exception.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder SetFileLoadExceptionHandler(this IConfigurationBuilder builder, Action<FileLoadExceptionContext> handler)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

builder.Properties[FileLoadExceptionHandlerKey] = handler;
return builder;
}

/// <summary>
/// Gets the default <see cref="IFileProvider"/> to be used for file-based providers.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static Action<FileLoadExceptionContext> GetFileLoadExceptionHandler(this IConfigurationBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

object handler;
if (builder.Properties.TryGetValue(FileLoadExceptionHandlerKey, out handler))
{
return builder.Properties[FileLoadExceptionHandlerKey] as Action<FileLoadExceptionContext>;
}
return null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.Extensions.FileProviders;
using System.Threading;
using Microsoft.Extensions.Primitives;

namespace Microsoft.Extensions.Configuration
Expand All @@ -31,7 +31,10 @@ public FileConfigurationProvider(FileConfigurationSource source)
{
ChangeToken.OnChange(
() => Source.FileProvider.Watch(Source.Path),
() => Load(reload: true));
() => {
Thread.Sleep(Source.ReloadDelay);
Load(reload: true);
});
}
}

Expand Down Expand Up @@ -61,9 +64,35 @@ private void Load(bool reload)
}
else
{
// Always create new Data on reload to drop old keys
if (reload)
{
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
using (var stream = file.CreateReadStream())
{
Load(stream);
try
{
Load(stream);
}
catch (Exception e)
{
bool ignoreException = false;
if (Source.OnLoadException != null)
{
var exceptionContext = new FileLoadExceptionContext
{
Provider = this,
Exception = e
};
Source.OnLoadException.Invoke(exceptionContext);
ignoreException = exceptionContext.Ignore;
}
if (!ignoreException)
{
throw e;
}
}
}
}
// REVIEW: Should we raise this in the base as well / instead?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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;

Expand Down Expand Up @@ -31,13 +32,34 @@ public abstract class FileConfigurationSource : IConfigurationSource
/// </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>
/// Will be called if an uncaught exception occurs in FileConfigurationProvider.Load.
/// </summary>
public Action<FileLoadExceptionContext> OnLoadException { get; set; }

/// <summary>
/// Builds the <see cref="IConfigurationProvider"/> for this source.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
/// <returns>A <see cref="IConfigurationProvider"/></returns>
public abstract IConfigurationProvider Build(IConfigurationBuilder builder);

/// <summary>
/// Called to use any default settings on the builder like the FileProvider or FileLoadExceptionHandler.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
public void EnsureDefaults(IConfigurationBuilder builder)
{
FileProvider = FileProvider ?? builder.GetFileProvider();
OnLoadException = OnLoadException ?? builder.GetFileLoadExceptionHandler();
}

/// <summary>
/// If no file provider has been set, for absolute Path, this will creates a physical file provider
/// for the nearest existing directory.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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;

namespace Microsoft.Extensions.Configuration
{
/// <summary>
/// Contains information about a file load exception.
/// </summary>
public class FileLoadExceptionContext
{
/// <summary>
/// The <see cref="FileConfigurationProvider"/> that caused the exception.
/// </summary>
public FileConfigurationProvider Provider { get; set; }

/// <summary>
/// The exception that occured in Load.
/// </summary>
public Exception Exception { get; set; }

/// <summary>
/// If true, the exception will not be rethrown.
/// </summary>
public bool Ignore { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
},
"netstandard1.3": {
"dependencies": {
"System.AppContext": "4.1.0-*"
"System.AppContext": "4.1.0-*",
"System.Threading.Thread": "4.0.0-*"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class IniConfigurationSource : FileConfigurationSource
/// <returns>An <see cref="IniConfigurationProvider"/></returns>
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
FileProvider = FileProvider ?? builder.GetFileProvider();
EnsureDefaults(builder);
return new IniConfigurationProvider(this);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class JsonConfigurationSource : FileConfigurationSource
/// <returns>A <see cref="JsonConfigurationProvider"/></returns>
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
FileProvider = FileProvider ?? builder.GetFileProvider();
EnsureDefaults(builder);
return new JsonConfigurationProvider(this);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ public override void Load(Stream stream)
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

var readerSettings = new XmlReaderSettings()
{
CloseInput = false, // caller will close the stream
DtdProcessing = DtdProcessing.Prohibit,
IgnoreComments = true,
IgnoreWhitespace = true
};
{
CloseInput = false, // caller will close the stream
DtdProcessing = DtdProcessing.Prohibit,
IgnoreComments = true,
IgnoreWhitespace = true
};

using (var reader = Decryptor.CreateDecryptingXmlReader(stream, readerSettings))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class XmlConfigurationSource : FileConfigurationSource
/// <returns>A <see cref="XmlConfigurationProvider"/></returns>
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
FileProvider = FileProvider ?? builder.GetFileProvider();
EnsureDefaults(builder);
return new XmlConfigurationProvider(this);
}
}
Expand Down
Loading