Freigeben über


La pesadilla de los web.config Gigantes

Básico

Como programadores siempre tenemos que lidiar con archivos de configuración, archivos XML destinados a la configuración de la aplicación, aunque varian un poco dependiendo del tipo d aplicación que hagamos, en su mayoría son exactamente lo mismo.

  • Web.config
  • App.config
  • Otros.config

En mi opinión son una hermosa forma de deshacernos de complejidad innecesaria en el código y desde luego una manera muy cool de crear aplicaciones fléxibles.

Sin embargo en aplicaciones de tipo empresarial comenzamos a tener detestables problemas con estos archivos. El notable aumento de complejidad en su estructura se convierte en una bomba de tiempo para el mantenimiento de las aplicaciones, es común encontrase con archivos de configuración que se miden en cientos de lineas y a veces en miles.

XML config file Nightmare

Cómo tener un archivo de configuración más limpio?

Por fortuna existe un mecanismo muy útil, a continuación les muestro un archivo de configuración que si bien no es uno precisamente enorme, ya comienza a mostrar problemasde legibilidad, en esta ocasión generados por la configuración de Unity Application Block , hablaremos de el en otro momento.

 <configuration>  
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
  </configSections>
  <unity xmlns="https://schemas.microsoft.com/practices/2010/unity">;
      <typeAliases>
        <!-- Lifetime manager types -->
        <typeAlias alias="singleton"
             type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,
                   Microsoft.Practices.Unity" />
        <typeAlias alias="external"
             type="Microsoft.Practices.Unity.ExternallyControlledLifetimeManager,
                   Microsoft.Practices.Unity" />
        <!-- User-defined Lifetime manager types -->
        <typeAlias alias="web_session"
            type="Traversal.Utilities.Web.WebSessionLifetimeManager,
                   Traversal" />
        <!-- Tipos para Domain.Core -->
        <typeAlias alias="IDataContextWrapper"  type="Domain.Core.Data.IDataContextWrapper`1, Domain.Core" />
        <typeAlias alias="IParameterManager"    type="Domain.Core.Repositories.IParameterManager, Domain.Core" />
        <typeAlias alias="ILocalizationManager" type="Domain.Core.Repositories.ILocalizationManager, Domain.Core" />

        <typeAlias alias="ContextWrapperSTE"    type="Domain.Core.Data.ContextWrapperSTE`1, Domain.Core" />
        <typeAlias alias="ContextWrapperEF"     type="Domain.Core.Data.ContextWrapperEF`1, Domain.Core" />
        <typeAlias alias="ParameterManager"     type="Domain.Core.Repositories.ParameterManager, Domain.Core" />
        <typeAlias alias="LocalizationManager"  type="Domain.Core.Repositories.LocalizationManager, Domain.Core" />

        <!-- Tipos para oplkjsdfkpwe.Core -->
        <typeAlias alias="IEnteManager"          type="Domain.wsldckm.Repositories.IEnteManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="IEquipoManager"        type="Domain.wsldckm.Repositories.IEquipoManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="IEstadioManager"       type="Domain.wsldckm.Repositories.IEstadioManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="IEventoManager"        type="Domain.wsldckm.Repositories.IEventoManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="IPartidoManager"       type="Domain.wsldckm.Repositories.IPartidoManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="ITagManager"           type="Domain.wsldckm.Repositories.ITagManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="ITipoFormacionManager" type="Domain.wsldckm.Repositories.ITipoFormacionManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="ITorneoEtapaManager"   type="Domain.wsldckm.Repositories.ITorneoEtapaManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="ITorneoNominaManager"  type="Domain.wsldckm.Repositories.ITorneoNominaManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="ITorneoManager"        type="Domain.wsldckm.Repositories.ITorneoManager, Domain.oplkjsdfkpwe" />

        <typeAlias alias="EnteManager"           type="Domain.wsldckm.Repositories.EnteManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="EquipoManager"         type="Domain.wsldckm.Repositories.EquipoManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="EstadioManager"        type="Domain.wsldckm.Repositories.EstadioManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="EventoManager"         type="Domain.wsldckm.Repositories.EventoManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="PartidoManager"        type="Domain.wsldckm.Repositories.PartidoManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="TagManager"            type="Domain.wsldckm.Repositories.TagManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="TipoFormacionManager"  type="Domain.wsldckm.Repositories.TipoFormacionManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="TorneoEtapaManager"    type="Domain.wsldckm.Repositories.TorneoEtapaManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="TorneoNominaManager"   type="Domain.wsldckm.Repositories.TorneoNominaManager, Domain.oplkjsdfkpwe" />
        <typeAlias alias="TorneoManager"         type="Domain.wsldckm.Repositories.TorneoManager, Domain.oplkjsdfkpwe" />
      </typeAliases>
      <containers>
        <container name="WebContainer">
          <types>
            <!-- Tipos para Domain.Core -->
            <type type="IParameterManager" mapTo="ParameterManager" />
            <type type="ILocalizationManager" mapTo="LocalizationManager" />
            <type type="IDataContextWrapper" mapTo="ContextWrapperEF">
              <lifetime type="web_session" />
            </type>
            <!--
              <type type="IDataContextWrapper" mapTo="ContextWrapperSTE">
                <lifetime type="web_session" />
              </type>-->
            <!-- Tipos para oplkjsdfkpwe.Core -->
            <type type="IEnteManager"           mapTo="EnteManager"         />
            <type type="IEquipoManager"         mapTo="EquipoManager"       />
            <type type="IEstadioManager"        mapTo="EstadioManager"      />
            <type type="IEventoManager"         mapTo="EventoManager"       />
            <type type="IPartidoManager"        mapTo="PartidoManager"      />
            <type type="ITagManager"            mapTo="TagManager"          />
            <type type="ITipoFormacionManager"  mapTo="TipoFormacionManager"/>
            <type type="ITorneoEtapaManager"    mapTo="TorneoEtapaManager"  />
            <type type="ITorneoNominaManager"   mapTo="TorneoNominaManager" />
            <type type="ITorneoManager"         mapTo="TorneoManager"       />
          </types>
        </container>

        <container name="StandAloneContainer">
          <types>
            <type type="IParameterManager" mapTo="ParameterManager" />
            <type type="ILocalizationManager" mapTo="LocalizationManager" />
            <type type="IDataContextWrapper"  mapTo="ContextWrapperEF" >
              <lifetime type="singleton" />
            </type>
          </types>
        </container>
      </containers>
    </unity>

  <appSettings>
    <add key="UnitySection" value="unity"/>
    <add key="UnityContainer" value="WebContainer"/>
  </appSettings>
   <compilation debug="true" targetFramework="4.0" />

    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login.aspx" timeout="2880" />
    </authentication>

    <membership>
      <providers>
        <clear/>
        <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ApplicationServices"
             enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
             maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
             applicationName="/" />
      </providers>
    </membership>

    <profile>
      <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/"/>
      </providers>
    </profile>

    <roleManager enabled="false">
      <providers>
        <clear/>
        <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/" />
        <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
      </providers>
    </roleManager>
  </system.web>
  <system.webServer>
     <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
  <!--LOT OF MORE STUFF-->
</configuration>

Toda sección posee un atributo configSource en el cual se puede indicar la ruta donde se encuentra definido el XML, asi que podemos extraer toda la sección de Unity en un archivo Unity.config , incluyendo la etiqueta y sus atributos, y el archivo de configuración quedaria de la siguiente forma, bastante más legible y manteniendo toda su funcionalidad...

Por no mencionar la abismal diferencia cuando se utilizan múltiples bloques...

 <configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
  </configSections>
  <unity configSource="Unity.config"  />

  <appSettings>
    <add key="UnitySection" value="unity"/>
    <add key="UnityContainer" value="WebContainer"/>
  </appSettings> 
  <system.webServer>
     <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

Y desde luego ese mismo atributo lo podemos usar en todas las secciones que superen un tamaño pequeño.

Enjoy it!