/// <summary> /// Get the string for a given key and convert to the desired type /// </summary> /// <param name="key">case insensitive key to fetch</param> /// <param name="default">An optional function to provide the default if the key is missing</param> /// <typeparam name="T">The type to convert to</typeparam> /// <returns>The converted value</returns> /// <exception cref="KeyNotFoundException">Key is not present and no default was provided</exception> public T Get <T>(string key, Func <T> @default = null) { var found = SettingsDictionary .TryGetValue(key, Configuration, out var result); if (found) { return(_converter.Convert <T>(result)); } else if (@default != null) { return(@default()); } else { throw new KeyNotFoundException(); } }
/// <summary> /// Populate the properties on a provided Type that match the keys in the SettingsDictionary. /// </summary> /// <param name="target">The object to set properties on</param> /// <param name="requireAll">All the properties on the target must be bound, otherwise an exception is thrown</param> /// <param name="prefix">Defaults typeof(T).Name</param> /// <typeparam name="T"></typeparam> public void Bind <T>(T target, bool requireAll = true, string prefix = null) where T : new() { prefix = prefix ?? typeof(T).Name; if (prefix.Length > 0) { prefix = prefix + "."; } var props = typeof(T) .GetProperties() .Where(p => !(p.GetSetMethod() is null)); foreach (var prop in props) { var type = prop.PropertyType; var name = $"{prefix}{prop.Name}"; object value = null; if (!type.IsPrimitive && !type.IsEnum && type != typeof(string) && type != typeof(decimal) && type != typeof(DateTime)) { value = this.GetType().GetMethods() .Where(x => x.Name == "Bind" && x.GetParameters()?.Length == 2) .FirstOrDefault()? .MakeGenericMethod(type)? .Invoke(this, new object[] { requireAll, name }); } else if (SettingsDictionary.TryGetValue(name, Environment, out var result) || requireAll) { value = Get(type, name); } if (!(value is null)) { prop.SetValue( target, value ); } } }
/// <summary> /// Method to retrieve value either from the settings dictionary or from cache. Uses recursion to get nested class values /// </summary> /// <param name="prop">Property of class that a value is to be returned</param> /// <param name="name">Name of settings dictionary key that is linked to property</param> /// <param name="preload">Determines if the call is from a preload event or not</param> /// <param name="requireAll">Determines if all properties of the binding class should have a matching settings dictionary key and value</param> /// <returns></returns> private IBindResult GetPropertyValue(PropertyInfo prop, string name, bool preload, bool requireAll) { object value = null; var errors = new List <string>(); if (IsNestedProperty(prop.PropertyType)) { // Get nested property value via recursion var valObjectResult = GetType() .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) .FirstOrDefault(x => x.Name == nameof(Settings.GetBindResult) && x.GetParameters()?.Length == 4)? .MakeGenericMethod(prop.PropertyType) .Invoke(this, new object[] { requireAll, name, false, errors }); if (valObjectResult is IBindResult typedResult) { if (typedResult.Errors?.Any() ?? false) { errors.AddRange(typedResult.Errors); } value = typedResult.Result; } } // When the call is not for a preload event and it is not nested, attempt to get a value else if (preload) { var getter = prop.GetGetMethod(); if (getter == null) { return(null); } value = getter.Invoke(this, Array.Empty <object>()); } // When the value is still null, last chance call to check if there is a value in the settings dictionary if (value is null && SettingsDictionary.TryGetValue(name, Environment, out var result) || requireAll) { value = Get(prop.PropertyType, name); } return(new BindResultBase(errors, value)); }