Pisane przez nas "biblioteki wielokrotnego użytku" mogą (a nawet: powinny być?) konfigurowalne. Konfiguracja musi być oczywiście rozprowadzana wraz z aplikacją korzystającą z takich bibliotek, więc obowiązkiem programisty aplikacji jest dołączenie do *config odpowiednich wpisów.
Swego czasu miałem krótki romans z tworzeniem własnych, dedykowanych "sekcji konfiguracji", ale długo on nie trwał. Toż to jakaś masakra!
W 90% (albo i więcej) przypadków wystarczają zwykle wpisy w appSettings. Jednak sekcja ta bardzo szybko może stać się śmietnikiem zawierającym cały syf tego świata.
Osobiście preferuję rozdzielanie konfiguracji między wiele plików, korzystając z bardzo przydatnego atrybutu configSource, który możemy nałożyć na każdą sekcję w *.config. Dzięki temu, dla przykładu, wszystkie connection stringi mogę wywalić do pliku conn.config:
<?xml version="1.0" encoding="utf-8" ?>
<connectionStrings>
<!– conn str 1 –>
<!– conn str 2 –>
<!– …………. –>
</connectionStrings>
a w "głównym" konfigu aplikacji tylko się do niego odwołać:
<connectionStrings configSource="conn.config" />
Tak samo chociażby z:
<system.serviceModel>
<bindings configSource="serviceModel_bindings.config" />
<client configSource="serviceModel_client.config" />
</system.serviceModel>
Równie dobrze można wyrzucić na zewnątrz każdą inną sekcję, w tym appSettings. Niestety nie można wyrzucić tylko jej części – wszystko albo nic. Takie wpisy nie są mergowane. Jeśli więc pozbędziemy się całości appSettings z głównego pliku, to po prostu przenosimy syf gdzieś indziej.
Chyba że… podzielimy appSettings na kilka części! A raczej: skorzystamy w naszej bibliotece z nowej sekcji, analogicznej do appSettings.
Jest to bardzo proste, nawet bez grzebania się we własne sekcje. Wystarczy w <configSections> dodać coś takiego:
<section name="myAppSettings" type="System.Configuration.AppSettingsSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false"/>
i już możemy korzystać z nowej sekcji tak jak z appSettings:
<myAppSettings>
<add key="somekey" value="somevalue"/>
<!– setting 2 –>
<!– setting 3 –>
<!– ………… –>
</myAppSettings>
A co za tym idzie: możemy na tym poziomie użyć configSource!
Czas na praktyczny przykład z życia wzięty…
Pisałem niedawno bibliotekę do komunikacji z zewnętrznym systemem, nazwijmy go CCM. Do tej biblioteki (na poziomie projektu dll!) dodałem plik konfiguracyjny ccm_settings.config. A w nim wszystko co jest potrzebne bibliotece do działania:
<?xml version="1.0" encoding="utf-8" ?>
<ccmSettings>
<add key="CCM.url" value="https://127.0.0.1:8443/axl/"/>
<!– setting 2 –>
<!– setting 3 –>
<!– ………… –>
</ccmSettings>
I tyle. Biblioteka sama w sobie oczywiście nie potrzebuje "głównego" configa.
Taki plik może służyć użytkownikowi biblioteki na kilka sposobów:
Po pierwsze: służy jako template, wzorzec. Programista wie jakie wartości trzeba uzupełnić i w jaki mniej więcej sposób.
Po drugie: programista może zdefiniować sobie w konfigu nową sekcję i umieścić wszystko co mojej bibliotece do szczęścia potrzebne nie syfiąc w appSettings.
Po trzecie: może to zrobić w osobnym pliku poprzez configSource.
Ale mało tego, jakże pomocne było mi to podczas tworzenia całego systemu! Równolegle z biblioteką implementowałem także jej klienta. I pisałem do niej oczywiście testy jednostkowe. Zarówno klient, jak i projekt z testami potrzebują konfiguracji. Aby nie mnożyć kopii tej samej konfiguracji, zdefiniowałem tą sekcję w konfiguracji klienta i testów, ustawiłem configSource:
<ccmSettings configSource="ccmSettings.config" />
, a właściwy plik dodałem do tych projektów jako link. Potem tylko "Copy to output directory" na "Copy always" lub "Copy if newer"… i tyle. Oczywiście to moje postępowanie bazuje na założeniu, że oryginalny plik zawiera poprawne wartości, działające w moim środowisku.
Należy pamiętać jednak, że w przypadku aplikacji www modyfikacja takiego pliku nie jest automatycznie brana pod uwagę, tzn nie powoduje restartu aplikacji (w przeciwieństwie do modyfikacji web.config lub zawartości /bin czy /app_code).
Jak się odwołujesz do tych sekcji w kodzie? Identycznie jak do AppSettings?
Tak, narzucam na to najwzyklejszą statyczna klasę i tam np: (int)ConfigurationManager.GetSection("….")["key"]
Nie zapominajmy również, iż bardzo ważną sprawą jest zabezpieczenie tychże informacji!
Szyfrowanie i deszyfrowanie opisałem tutaj ( http://kurzyniec.pl/artykuly/zabezpieczenie-pliku-webconfig ) i tutaj ( http://kurzyniec.pl/artykuly/secure-config ) – może się komuś przydać ;)