Vlatkovic.NET

Web site o tehnologiji, programiranju, muzici, kuvanju, knjigama, u stvari o svemu i svačemu...
Dizajn: www.studio7designs.com opensource web templates.


Najnovije na blogu

 Saturday, 22 December 2007

Ljudi obožavaju da trpaje sve živo u web.config (uopšte u .config fajlove). To je u principu ok ali ako naletite na projekat u kome ima preko stotinu raznih vrednosti koje treba podesiti da bi radilo onda 'oće i muka da pripadne. E sad, od količine ne može da se pobegne ali ako se ta količina organizuje i sistematizuje onda podešavanje web.cofig-a i ne mora da bude tako mučna operacija. Ovo je moj skromni predlog kako da se reši problem.

Konfiguracioni fajlovi u .Net (web.config, app.config) podržavaju simpatičnu stvar koja se zove <configSections>. Unutar ovih tagova možete proizvoljno da definišete <sectionGroups> i <section> tagove. Ovo znači da možete da kreirate bilo kakvu logičku strukturu podataka i svom .config fajlu. Pretpostavimo da radimo WEB projekat koji u solution-u sadrži DAL (data access layer) projekat zadužen da komunicira sa bazom i tumba podatke i BLL (business logic layer) koji sadrži neku poslovnu logiku između DAL i WEB projekta.

solExpl.gif

Svaki od ovih projekata može potencijalno da vuče vrednosti iz konfiguracionog fajla. Na primer, očigledno je da DAL projektu (onaj što se igra sa bazom) treba string koji mu govori gde je baza podataka i koju bazu gađa. Ili, projektu BLL onaj koji sadrži neku logiku treba podatak sa kog web servisa recimo da vuče neke podatke. Da te tražene vrednosti ne stoje samo kao lista poželjno je da napravimo strukturu koja će odražavati našu logičku organizaciju. I evo primera kako može da izgleda:


Ovako počinje web.config

<configSections>
        <sectionGroup name="VlatkovicNET.TestApp">
            <!-- Common -->
            <section name="CommonApp" type="System.Configuration.NameValueSectionHandler"/>
            <!-- Util -->
            <section name="Util" type="System.Configuration.NameValueSectionHandler"/>
            <sectionGroup name="DAL">
                <section name="ConnectionStrings" type="System.Configuration.NameValueSectionHandler"/>
                <section name="StoragePaths" type="System.Configuration.NameValueSectionHandler"/>
                <section name="XMLStorage" type="System.Configuration.NameValueSectionHandler"/>
            </sectionGroup>
            <sectionGroup name="BLL">
                <section name="WebServices" type="System.Configuration.NameValueSectionHandler"/>
                <section name="CacheServers" type="System.Configuration.NameValueSectionHandler"/>
            </sectionGroup>
        </sectionGroup>
    </configSections>

Šta ovde imamo? Prvi blok “definicija sekcija početak” odnosi se na kreiranje strukture podataka koja će se upisati u .config fajl. Poželjno je da struktura konfiguracije prati strukturu aplikacije. <configSections> definiše grupu koja nosi ime sastavljeno od imena Namespace-a i imena projekta “VlatkovicNET.TestApp” koja u sebi pak definiše grupe koje će da sadrže podešavanja karakteristična za BLL i DAL projekte. Type=”System.Configuration.NameValueSectionHandler” unutar definicije sekcija govori koji handler će da obrađuje našu strukturu parova. U ovom slučaju to je .NET handler NameValueSectionHandler . Možete da pišete i svoj ali to nije predmet ovog teksta. I još jedna napomena, struktura ovde nema nikakve veze sa funkcionalnošću već samo sa organizacijom i preglednošću.

Sledeći blok su same definicije vrednosti za prethodno definisanu strukturu. Na primer:

    <VlatkovicNET.TestApp>
        <CommonApp>
            <add key="CompanyName" value="VlatkovicNET"></add>
            <add key="ContactEmail" value="pera@test.com"></add>
            <add key="SmtpServer" value="127.0.0.1"></add>
        </CommonApp>
        <Util>
            <add key="HelpUrl" value="http://www.helpme.com"></add>
            <add key="Language" value="en"></add>
        </Util>
        <DAL>
            <ConnectionStrings>
                <add key="MSSql"    value="..."></add>
                <add key="MySql"    value="..."></add>
                <add key="Oracle"    value="..."></add>
            </ConnectionStrings>
            <StoragePaths>
                <add key="Images"    value="..."></add>
                <add key="Docs"    value="..."></add>
            </StoragePaths>
            <XMLStorage>
                <add key="path1"    value="..."></add>
                <add key="path2"    value="..."></add>
                <add key="path3"    value="..."></add>                        
            </XMLStorage>
        </DAL>
        <BLL>
            <WebServices>
                <add key="service1" value="http://server1/service1.asmx" ></add>
                <add key="service2" value="http://server1/service2.asmx"></add>
            </WebServices>
            <CacheServers>
                <add key="server1" value="cacheServer1" ></add>
                <add key="server2" value="cacheServer2"></add>
            </CacheServers>
        </BLL>
    </VlatkovicNET.TestApp>

Primer:

<VlatkovicNET.TestApp>
   <DAL>
      <ConnectionStrings>
         <add key="MSSql" value="..."></add>

unutar projekta DAL koji pripada namespace-u VlatkovicNET.TestApp definišemo grupu ConnectionStrings koja sadrži vrednost za MSSql connection string.


Sada ostaje da samo pročitamo ove vrednosti iz .config fajla. Jednostavno, pošto je .config xml file dovoljno je da definišemo XPath izraz do svakog ključa (key) a njegova vrednost je value iz NameValue para koga ćemo da čitamo. Ako niste bliski sa XPath-om nedajte da vas to obeshrabri, bar u ovom slučaju on je jako jednostavan a i nije loše da se nešto novo nauči (brrr ne sviđa mi se ova rečenica, učite ono što volite). So, evo ga primer:


Prvo definišemo metode za dobijanje vrednosti:

    public class ConfigurationManager
    {
        private static object lock_GetIntFromConfig = new object();

        /// <summary>
        /// Gets the int from config.
        /// </summary>
        /// <param name="path2Key">The path to key.</param>
        /// <param name="key">The key.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static int GetIntFromConfig(string path2Key, string key, int defaultValue)
        {
            lock (lock_GetIntFromConfig)
            {
                string val = GetValueFromConfig(path2Key, key);

                if (val != null)
                    int.TryParse(val, out defaultValue);

                return defaultValue;
            }
        }


        private static object lock_GetDecimalFromConfig = new object();
        /// <summary>
        /// Gets the decimal from config.
        /// </summary>
        /// <param name="path2Key">The path to key.</param>
        /// <param name="key">The key.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static decimal GetDecimalFromConfig(string path2Key, string key, decimal defaultValue)
        {
            lock (lock_GetDecimalFromConfig)
            {
                string val = GetValueFromConfig(path2Key, key);

                if (val != null)
                    decimal.TryParse(val, out defaultValue);

                return defaultValue;
            }
        }



        private static object lock_GetValueFromConfig = new object();
        /// <summary>
        /// Gets the value from config.
        /// </summary>
        /// <param name="path2Key">The path to key.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public static string GetValueFromConfig(string path2Key, string key)
        {
            lock (lock_GetValueFromConfig)
            {
                NameValueCollection nvc = (NameValueCollection)ConfigurationSettings.GetConfig(path2Key);
                if (nvc != null)
                    return nvc[key];
                else
                    return null;
            }
        }
    }

Primer: vraćanje vrednosti specifičnog connection stringa za projekat
GetValueFromConfig("VlatkovicNET.TestApp/DLL/ConnectionStrings", "MSSql");

Primer: direktno uzimanje int vrednosti iz .config-a
GetIntFromConfig("VlatkovicNET.TestApp/BLL/Timeouts", "Cache", 5)

Kao što vidite XPath izraz se piše jednostavno, sa znakom “/” odvajate grupe i dodajete sekciju na kraju. I metodama prosledite XPath i key koji obeležava vrednost.

Metode ispisane ovde možete po želji da stavite u osnovnu klasu za vaše strane i kontrole ili u neki zaseban framework koji ćete da vučete sa sobom gde god da krenete. Ovde možete da skinete primer cele aplikacije pa da pogledate. Primer je samo ilustracija, nije reprezentativan :-) Primer možete da proširujete normalno pa da recimo kreirate malli generator klase koja opisuje vašu konfiguraciju u .config fajlu. Gledaću da u budućnosti napišem pravi generator sa code DOM.

Za kraj, nadam se da nisam izmislio toplu vodu i udavio vas u njoj :-) I da ne zaboravim, zahvaljujem se gospodinu Dejanu Miličiću na recenziji i više nego korisnim savetima.

Primer code-a

Saturday, 22 December 2007 13:52:10 (Central Europe Standard Time, UTC+01:00)
All comments require the approval of the site owner before being displayed.
Name 
E-mail
Home page

Comment (HTML not allowed)

Enter the code shown (prevents robots):