11 czerwca 2008

Konfiguracja aplikacji - własne podejście

Każda aplikacja nie może się obyć bez konfiguracji. W ASP.NET mamy do dyspozycji web.config, który z jednej strony zawiera ustawienia aplikacji dotyczące platformy, framework'a, a z drugiej własne ustawienia aplikacji - appSettings. Czasami zdarza się, że tych ustawień może być bardzo dużo co powoduje zmniejszoną czytelność i pomieszanie z ustawieniami systemowymi. Dlatego też w każdej aplikacji, którą tworzę korzystam z własnego mechanizmu przechowywania ustawień. Rozdzielenie tych ustawień powoduje, że mój plik web.config zawiera podstawowe tagi frameworka, które są niezależne od aplikacji.

Stworzyłem zatem prostą klasę AppConfig, która implementuje znany pattern Singleton. W chwili pierwszego skorzystania z obiektu klasa zostaje zainicjalizowana i zostają załadowane wszystkie ustawienia aplikacji.

private static AppConfig config = null;

public static AppConfig Current
{
    get
    {
        if(config == null)
        {
            lock(typeof(AppConfig))
            {
                if(config == null)
                {
                    config = new AppConfig();
                }
            }
        }
        return config;
    }
}

Ustawienia zapisywane są w prostym pliku XML o stałej nazwie 'application.xml', który zawiera proste elementy.


<config>
    <!-- Configuration for production server -->
    <application.name>Project name</application.name>

    <shared.upload-directory>C:\Project\shared</shared.upload-directory>
</config>

Do załadowania danych użyłem poprostu XMlDocument, który ładuje wszystkie tagi pokolei do kolekcji. Dodatkowo konfigurator szuka pliku o nazwie 'application.machineName.xml', gdzie dla danej maszyny można nadpisać ustawienia z podstawowego pliku konfiguracyjnego. I tak dla przykładu w podstawowym pliku mamy ustawienia takie jakie obowiązują w środowisku produkcyjnym. W plikach rozszerzających mamy konfigruację, na przykład developerską, która jest prywatna dla maszyny danego developera.

Każdy programista w zespole może stworzyć własną konfigurację nadpisując tylko te pozycje, które wymagają zmiany do uruchomienia aplikacji na danej maszynie. Przykładowo połączenie do bazy danych, z którą komunikuje się aplikacja może być zupełenie inne niż na produkcji, czy u innego programisty.

Pobranie danego ustawienia wymaga podania konkretnego klucza oraz typu danych jaki spodziewamy się uzyskać. Mamy do wyboru dwie metody. Pierwsza oczekuje istnienia parametru w konfiguracji. Jeśli podanego parametru nie ma to zostanie wyrzucony wyjątek. Druga metoda pozwala na podanie wartości domyślnej i oznacza to, że parametr jest niewymagany i nie musi istnieć w konfiguracji. Dopisanie takiego parametru przesłoni domyślną wartość podaną w kodzie.

public T GetItem<T>(string key)
{
    lock (locker)
    {
        if (!items.ContainsKey(key))
        {
            throw new NotImplementedException(String.Format("The key '{0}' is not defined in application config file.", key));
        }
        return (T)Convert.ChangeType(items[key], typeof(T));
    }
}

public T GetItem<T>(string key, T defaultValue)
{
    lock (locker)
    {
        if (items.ContainsKey(key))
        {
            return (T)Convert.ChangeType(items[key], typeof(T));
        }
        return defaultValue;
    }
}

A poniżej przykład pobrania wartości:

string _name = AppConfig.Current.GetItem<string>("application.name");

Niektórzy mogą zapytać po co takie zmyślne i długie nazwy parametrów. Otóż mam w tym dwa cele. Po pierwsze parametr jest dobrze opisany i nazwa tagu odrazu wiele mówi. Po drugie dzielenie nazwy kropką na segmenty daje mi możliwość udoskonalenia mechanizmu, ale o tym w kolejnym poście. :)

Kod źródłowy całej klasy AppConfig  - zapraszam do pobrania i korzystania/eksperymentowania. :)

PS
Na fali ostatnich wydarzeń post ten ukazał się tylko na zine.net.pl i nie jest dostępny na mojej prywatnej stronie. :)

PS2
Kod AppConfig używa kilka dodatkowych elementów i bez nich się nie kompiluje, ale zamieszczam go tutaj raczej w celach ogólno-poglądowych całego rozwiązania.

Filed under: ,
Attachment(s):AppConfig.txt
 

Komentarze:

# nuwanda said:

Czy Michał i Ciebie przeciąga na jasną stronę zina?

11 czerwca 08 at 22:11
# dario-g said:

Ja jestem po jasnej stronie od zawsze :)))))

11 czerwca 08 at 22:24
# apl said:

Może warto dodać, że system konfiguracji .NET Framework udostępnia podobny mechanizm prosto z pudełka. Podstawą rozwiązania jest możliwość wydzielenia sekcji <appSettings> do osobnego pliku:

<appSettings file="application.config">

<!-- jakieś dodatkowe ustawienia -->

</appSettings

To oczywiście rozwiązuje problem tylko częściowo, gdyż sekcja <appSettings> może znajdować się w pliku konfiguracyjnym tylko raz, a więc moglibyśmy dołączyć tylko jeden zewnętrzny plik konfiguracyjny. Taki scenariusz nas nie interesuje. Nic nie stoi jednak na przeszkodzie, by utworzyć nową sekcę typu AppSettingsSection, tyle że pod inną nazwą, np. <application>:

<configSections>

   <section name="application" type="System.Configuration.AppSettingsSection" />

</configSections>

<application file="application.config" />

W zewnętrznym pliku application.config możemy teraz zawrzeć nasze ustawienia:

<application>

  <add key="application.name" value="Project name" />

  <add key="shared.upload-directory" value="C:\Project\shared" />

</application>

Teraz w kodzie możemy odwołać się do sekcji <application> tak, jakby była zdefiniowana bezpośrednio w głównym pliku konfiguracyjnym:

NameValueCollection application = (NameValueCollection) ConfigurationManager.GetSection("application");

string name = application["application.name"];

P.S. Ciekawe, czy system komentarzy nie obrazi się na mnie za te wszystkie nawiasy kątowe?

12 czerwca 08 at 02:02
# Ww.CodeSculptor said:

Wczoraj przeczytaLem post Darka omawiający hierarchiczne pliki konfiguracyjne . Podczas czytania naszły

12 czerwca 08 at 11:42
Komentarze anonimowe wyłączone