Settings.settings vs. App.config, the best of both worlds

 
11 september 2008

Bij het ontwikkelen van vrijwel elk stuk software wil je graag dat instellingen configureerbaar opgeslagen en beheerd kunnen worden. Aan de ene kant wil je naar buiten toe dat een applicatie configureerbaar is voor de eindgebruiker, bijvoorbeeld welke server er wordt gebruikt voor de databaseconnectie. Aan de andere kant wil je binnen de applicatie zelf een library project kunnen configureren, bijvoorbeeld welke NHibernate instellingen worden er gebruikt.

Als developer heb ik een aantal requirements met betrekking tot het omgaan met instellingen:

  • Instellingen kunnen automatisch switchen afhankelijk van de gekozen configuratie (bijvoorbeeld Debug- / Release-versies van connection strings).
  • Instellingen zijn sterk getypeerd. 
  • Na installatie zijn de instellingen makkelijk aan te passen (dmv een xml file al dan niet configureerbaar met behulp van de UI).
  • Hiërarchische instellingen zijn mogelijk (subsettings).

C# heeft icm Visual Studio hiervoor 2 verschillende methoden:

  • een settings file (Settings.settings)
  • een application configuration file (App.Config)

Deze twee methoden lijken beide niet aan mijn requirements te voldoen, hieronder zal ik uiteenzetten waarom, verder zal ik een een mogelijk compromis voorstellen.

App.Config
De meest simpele manier van het opslaan van instellingen is het in de <appSettings> van de App.config toevoegen van key / value pairs. Hierna zijn ze op te halen dmv de static System.Configuration.ConfigurationManager class.

Voordelen:

  • De app.config instellingen gedefinieerd in het startup project zijn over de hele solution te benaderen via de static ConfigurationManager class.
  • Er is alleen een referentie nodig naar System.Configuration.
  • Deze methode resulteert uiteindelijk in een [ApplicationName].config file welke door de gebruiker na installatie aan te passen is.

Nadelen:

  • Values kunnen niet sterk getypeerd worden opgehaald en moeten hierdoor bij elke aanroep handmatig worden geconverteerd.
  • Alleen de instellingen gedefinieerd in het startup project worden meegenomen naar de uiteindelijke [ApplicationName].config file. Als je dus een testprojectje hebt wat je tijdelijk als startup project wilt gebruiken dan kan je dus ineens niet meer bij je instellingen.
  • De App.Config file heeft geen codebehind file, hierdoor is het programmatisch switchen van instellingen op basis van bijvoorbeeld het gekozen buildtype niet mogelijk.
  • Er zijn geen hiërarchische instellingen mogelijk (subsetting).

Er is verder de mogelijkheid om een eigen configSection te creëren in de app.config. Hiermee is het wel mogelijk de hiërarchische instellingen sterk getypeerd op te halen, maar hiervoor moet een hoop code geschreven worden. Dit wordt echter sinds .Net 2.0 voor een deel opgelost met de .settings files.

Settings.setting
Aan een project kun je een .settings file toevoegen door in het property scherm van het project te klikken op het settings tabblad. Of je kiest voor een “Add – New item – Settings file”. Na het toevoegen van een instelling is deze, afhankelijk van de locatie van de file, opvraagbaar via:

  • Namespace.Properties.SettingsFilename.Default.PropertyNaam
  • Namespace.SettingsFilename.Default.PropertyNaam

Het grote voordeel van deze methode is dat na het rebuilden van het project de instellingen sterk getypeerd aanwezig zijn. Hiermee voorkom je het probleem dat je tijdens runtime erachter komt dat een instelling niet correct is gedefinieerd / aangeroepen. Verder wordt de code die hiervoor nodig is automatisch gegenereerd.

Voor elke gecreëerde .settings file worden de instellingen toegevoegd aan de App.config file dmv een configSection en zijn hierna dus ook door de gebruiker te editen.

Voordelen:

  • De scope (user / application) is instelbaar.
  • Er kunnen allerlei instellingen types (color / font / timespan) via de Visual Studio UI worden ingesteld via een type-specifieke inputmethode.
  • De .setting file heeft een Code behind file waarin logica mbt de opgeslagen instellingen kan plaatsvinden.

Nadelen:

  • Er zijn geen hiërarchische instellingen mogelijk.
  • Er is een referentie nodig naar de assembly waar de .settings file zich in bevind.

Dit laatstgenoemde nadeel is niet gemakkelijk overwonnen. In een standaard 3 tier applicatie (UI / Domein / Repository) is de UI meestal het startup project en wordt hier dus de app.config gevuld. Het UI project heeft echter vaak een referentie naar andere projecten welke op hun beurt weer instellingen willen gebruiken. Dit is niet mogelijk vanwege het ontstaan van circulaire referenties.

Wat zou ideaal zijn?
Bovenstaande twee oplossingen voldoen niet aan mijn requirements. Welke aanpassingen zou ik graag willen zien in een nieuwe versie van Visual Studio:

  • alle .settings files zijn cross-project benaderbaar via de ConfigurationManager static class;
  • ondersteuning voor sterk getypeerde hiërarchische instellingen in het settings scherm van de Visual Studio UI;
  • een optie op de app.config file welke aangeeft of deze meegenomen wordt in de uiteindelijke exe.config file in de output dir;

Compromis: Settings as a Service
Leuk zo’n wensenlijstje, maar wat er niet in zit, zit er (nog) niet in. Tijd om zelf een oplossing voor te dragen die een aantal voordelen combineert:

Door de instellingen te definiëren in een .setting file in een extern “instellingen” project en de access modifier van de .settings file op “public” te zetten lossen we een aantal zaken op:

  • Het circulaire referentie probleem is opgelost.
  • De instellingen zijn te editen met de Visual Studio UI.
  • De instellingen blijven sterk getypeerd.
  • Het maakt het niet meer uit wat het startup project is.
  • Dmv de code behind file zijn instellingen uit te wisselen afhankelijk van de build configuratie.

Het is echter nog niet helemaal koek en ei:

  • Deze methode produceert geen .config file die na installatie door een gebruiker te editen is.
  • Het is in Visual Studio mogelijk om properties te databinden aan een instelling, bijvoorbeeld de startup size van een form. Dit is in de Visual Studio UI te doen echter hier kan niet gekozen worden voor instellingen die ge-exposed zijn in een extern project. Dit is weliswaar te compenseren door de gegenereerde code te editen, maar voelt niet goed.
  • Door de instellingen op te halen uit een extern project verbreek je normaal geldende instellingen hiërarchie van webpagina’s (machine.config < web.config < subdir/web.config).
  • Het is nog steeds niet mogelijk om hiërarchische instellingen op te slaan.

Hoe doen anderen het?
Sommige mensen gooien alles overboord en gaan zelf aan de slag met het bouwen van een eigen oplossing:

Voor het aanpassen van .settings files afhankelijk van de gekozen configuratie gebruiken mensen ook wel tools zoals Msbuild community tasks’ XmlMassUpdate om instellingen pre-build uit te wisselen.

Als je kijkt naar library projects zoals NHibernate en Log4Net, exposen deze hun instellingen dmv een custom configSection. Deze hebben dus zelf hun classes geschreven voor het afhandelen van de configSection.

Al met al zie ik hier deeloplossingen en geen oplossing die alle requirements dekt. Momenteel ben ik bezig met het doorploegen van dit mega-artikel over de System.Configuration class om te kijken of daar ergens nog een geweldige oplossing verscholen ligt.

In de tussentijd… deel aub uw ervaringen op het gebied van het omgaan met instellingen en configuratie!


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Development, .Net

Tags: , , , , , , ,


Reacties (4)

  • Andries schreef:

    Misschien is Nini iets voor u?

    http://sourceforge.net/projects/nini

    Geplaatst op 17 juni 2009 om 13:01 Permalink

  • Rick schreef:

    @Chris

    Assemblies die niet al in de database moeten zijn, moeten er dan inderdaad ineens 1 hebben. Eerlijk gezegd is de situatie hier meer dat de settings door een bepaalde assembly uitgelezen worden en middels een interne API aangeboden (of opgedrongen) worden aan de andere assemblies. Daarnaast gaat het om een website, waarbij de database access eigenlijk wel al op top-level geregeld is.

    Maar het betekent dus wel dat er een soort library ontwikkeld is om die settings uit te lezen en beschikbaar te stellen. Dat is natuurlijk wel ontwikkel- en onderhoudswerk.

    Geplaatst op 16 september 2008 om 11:29 Permalink

  • Chris de Kok schreef:

    @Rick:

    Volgens mij zit je nu nog met hetzelfde probleem. De connectionstring moet bekend zijn bij de elke assembly die de settings willen lezen. En die connectionstring wil je juist op één plek configureren. Hoe los je dit op?

    Geplaatst op 16 september 2008 om 9:18 Permalink

  • Rick schreef:

    Waar ik nu werk, gebruiken ze settings in de database voor heel veel data-gerelateerd instellingen (dus niet voor de applicatie als geheel). Niet ideaal, ovoverigens.

    Belangrijkste voordeel: zowel lokaal als remote (gaat hier om web apps) makkelijke benaderbaar en alle assemblies hebben met de connectionstring voldoende om de settings te lezen

    Belangrijkste nadelen: niet makkelijk mee te updaten bij het plaatsen van een nieuwe release en het is niet heel praktisch om die settings met database tools aan te moeten passen.

    Geplaatst op 12 september 2008 om 11:17 Permalink