Last active
September 29, 2017 17:05
-
-
Save randyburden/2cc72c8f52e97646868e2a9b643987a8 to your computer and use it in GitHub Desktop.
C# strongly named application settings/options implementation. Can be used with Dependency Injection or with static helper method AppSettingsOptionsProvider<T>.Bind();.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Configuration; | |
| using System.Reflection; | |
| namespace Utilities.Options | |
| { | |
| /// <summary> | |
| /// Uses <see cref="ConfigurationManager"/> to populate options. | |
| /// Finds setting names/keys by <see cref="OptionNameAttribute"/>, {PropertyName}, or {ClassName}.{PropertyName}. | |
| /// </summary> | |
| /// <typeparam name="T">The type of options being requested.</typeparam> | |
| public class ConfigurationManagerOptionsProvider<T> : IOptions<T> where T : class, new() | |
| { | |
| /// <summary> | |
| /// The default configured TOptions instance. | |
| /// </summary> | |
| public T Value | |
| { | |
| get | |
| { | |
| return Bind(); | |
| } | |
| } | |
| /// <summary> | |
| /// Binds the <see cref="ConfigurationManager"/> values to the options being requested. | |
| /// </summary> | |
| /// <remarks> | |
| /// Uses the following patterns when locating a setting by name/key: | |
| /// Option 1: [OptionNameAttribute] | |
| /// Option 2: {PropertyName} | |
| /// Option 3: {ClassName}.{PropertyName} | |
| /// | |
| /// It is recommended to use option 3 as it is more explicit and reduces setting name/key collisions. | |
| /// </remarks> | |
| /// <returns>Configured options instance.</returns> | |
| public static T Bind() | |
| { | |
| var type = typeof(T); | |
| var instance = Activator.CreateInstance<T>(); | |
| var properties = type.GetProperties(); | |
| foreach (var property in properties) | |
| { | |
| if (!property.CanWrite) | |
| { | |
| continue; | |
| } | |
| var settingType = property.PropertyType == typeof(ConnectionStringSettings) ? SettingType.ConnectionString : SettingType.AppSetting; | |
| string settingName = null; | |
| object settingValue = null; | |
| // Try to get setting value by OptionNameAttribute | |
| settingName = GetOptionNameAttributeValue(property); | |
| if (settingName != null) | |
| { | |
| settingValue = GetSetting(settingName, settingType); | |
| } | |
| // Try to get setting name by property name | |
| if (settingValue == null) | |
| { | |
| settingName = property.Name; | |
| settingValue = GetSetting(settingName, settingType); | |
| } | |
| // Try to get setting name by {ClassName}.{PropertyName} | |
| if (settingValue == null) | |
| { | |
| settingName = $"{type.Name}.{property.Name}"; | |
| settingValue = GetSetting(settingName, settingType); | |
| } | |
| if (settingValue == null) | |
| { | |
| continue; | |
| } | |
| var propertyType = property.PropertyType; | |
| object propertyValue; | |
| try | |
| { | |
| propertyValue = Convert.ChangeType(settingValue, property.PropertyType); | |
| } | |
| catch (Exception e) | |
| { | |
| throw new Exception($"An error occurred while converting AppSettings key '{settingName}' to type '{property.PropertyType.Name}' for type '{type.FullName}'", e); | |
| } | |
| try | |
| { | |
| property.SetValue(instance, propertyValue); | |
| } | |
| catch (Exception e) | |
| { | |
| throw new Exception($"An error occurred while setting the value of property '{property.Name}' to value '{propertyValue}' for type '{type.FullName}'", e); | |
| } | |
| } | |
| return instance; | |
| } | |
| private static string GetOptionNameAttributeValue(PropertyInfo property) | |
| { | |
| var attributes = property.GetCustomAttributes(typeof(OptionNameAttribute), true); | |
| if (attributes != null && attributes.Length > 0) | |
| { | |
| var attribute = (OptionNameAttribute)attributes[0]; | |
| if (!string.IsNullOrWhiteSpace(attribute.OptionName)) | |
| { | |
| return attribute.OptionName; | |
| } | |
| } | |
| return null; | |
| } | |
| /// <summary> | |
| /// Gets the string value of a setting by name. | |
| /// </summary> | |
| /// <param name="settingName">Name of setting.</param> | |
| /// <returns>Value of setting.</returns> | |
| private static object GetSetting(string settingName, SettingType settingType) | |
| { | |
| if (settingType == SettingType.ConnectionString) | |
| { | |
| var connectionStringSettings = ConfigurationManager.ConnectionStrings[settingName]; | |
| return connectionStringSettings; | |
| } | |
| var settingValue = ConfigurationManager.AppSettings[settingName]; | |
| return settingValue; | |
| } | |
| private enum SettingType | |
| { | |
| AppSetting, | |
| ConnectionString | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| namespace Utilities.Options | |
| { | |
| /// <summary> | |
| /// Option/setting name to use when locating a value for the property. | |
| /// </summary> | |
| [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] | |
| public class OptionNameAttribute : Attribute | |
| { | |
| /// <summary> | |
| /// Option/setting name to use when locating a value for the property. | |
| /// </summary> | |
| public virtual string OptionName { get; private set; } | |
| /// <summary> | |
| /// Option/setting name to use when locating a value for the property. | |
| /// </summary> | |
| /// <param name="optionName">Option/setting name to use when locating a value for the property.</param> | |
| public OptionNameAttribute(string optionName) | |
| { | |
| OptionName = optionName; | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| namespace Utilities.Options | |
| { | |
| /// <summary> | |
| /// Used to retrieve configured TOptions instances. | |
| /// </summary> | |
| /// <remarks> | |
| /// Source/Inspiration: https://github.com/aspnet/Options/blob/dev/src/Microsoft.Extensions.Options/IOptions.cs | |
| /// </remarks> | |
| /// <typeparam name="TOptions">The type of options being requested.</typeparam> | |
| public interface IOptions<out TOptions> where TOptions : class, new() | |
| { | |
| /// <summary> | |
| /// The default configured TOptions instance. | |
| /// </summary> | |
| TOptions Value { get; } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment