pondělí 20. června 2016

Načtení objektu z konfiguračního souboru

V poslední době jsem narazil na pár projektů, kde jsou konfigurační hodnoty zapsány ve formátu, kterému pracovně říkám "flat".  Programátor nepoužil některou z obvyklých technik, ale místo toho v klíči  elementu v appSettings zachycuje částečně strukturu objektu a třídy a  poté určuje její hodnotu v atributu value.


Například tomuto zápisu:

    <appSettings>
        <add key="idnes.Uri" value="http://idnes.cz"/>
        <add key="idnes.Name" value="idnes"/>
        <add key="idnes.RetryAttempts" value="5"/>
        <add key="idnes.ConnectionPoint.Limit" value="5"/>
        <add key="idnes.ConnectionPoint.UseAlgorithm" value="true"/>
    </appSettings>

odpovídají následující třídy:

class EndPoint
{
    public string Uri { get; set; }
    public string Name{ get; set; }
    public int RetryAttempts { get; set; }
    public ConnectionPoint ConnectionPoint{ get; set; }
}
 
class ConnectionPoint
{
    public int Limit { get; set; }
    public bool UseAlgorithm { get; set; }
}

Tyto třídy se v kódu vyplňují  postupně, tedy asi nějak takto:

var endpoint = new EndPoint();
endpoint.Uri = ConfigurationManager.AppSettings.Get("idnes.Uri");
endpoint.RetryAttempts = int.Parse(ConfigurationManager.AppSettings.Get("idnes.RetryAttempts"));

a tak dále, co property, to řádek. Abych si ulehčil práci, napsal jsem si krátkou třídu, která umí podobné objekty sama naplnit. Má jen dvě metody a vypadá takto:

public class FlatConfigurationManager
{
    public static T CreateAndSetInstance<T>(string appSettingKeyName, Func<string, string> getValues)
    {
        var instance = Activator.CreateInstance<T>();
        SetInstance(instance, appSettingKeyName, getValues);
 
        return instance;
    }
 
    public static void SetInstance(object instance, string appSettingKeyName, Func<string, string> getValues)
    {
        var properties = instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
 
        foreach (var property in properties)
        {
            var keyName = $"{appSettingKeyName}.{property.Name}";
            dynamic value;
 
            if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
            {
                value = Activator.CreateInstance(property.PropertyType, null);
                SetInstance(value, keyName, getValues);
            }
            else
                value = getValues(keyName);
 
            property.SetValue(instance, Convert.ChangeType(value, property.PropertyType), null);
        }
    }
}

Pro práci s ní pak stačí jen zavolat:

Func<string,string> getValue = (name) => ConfigurationManager.AppSettings.Get(name);
 
var endpointIdnes = FlatConfigurationManager.CreateAndSetInstance<EndPoint>("idnes", getValue);

Funkce se předává jednoduše proto, že "rodina" ConfigurationManagerů se nám rozrostla, mimo klasické třídy ConfigurationManager  existuje třeba i CloudConfigurationManager. Takto se obejde nutnost definice společného interfejsu a wrapperů.

Žádné komentáře:

Okomentovat