Contar Líneas de Código

Nasa Contar líneas de código fuente es una práctica que se viene realizando desde tiempos inmemoriales… vamos desde antes de 1970 más o menos. Por ejemplo, de acuerdo con el Centro Tecnológico de Calidad de Software (Software Assurance Technology Center – SATC) de la NASA:

“El tamaño es una de las formas más antiguas y comunes de medición de software. El tamaño de los módulos es en sí misma un indicador de calidad. El tamaño puede ser medido a través del total de líneas de código, contando todas las líneas; que no correspondan a comentarios y que no esté en blanco, disminuyendo las líneas totales, por el número de espacios en blanco y comentarios, y todas aquellas sentencias ejecutables que se definen por un delimitador dependiente del lenguaje de programación.”

Sin embargo, con el paso del tiempo y medida que nuestra disciplina evolucionaba y maduraba, se determinó que por sí sólo el número de líneas de código en un proyecto de software no es un indicador de la calidad del mismo, o la productividad del trabajo realizado por los desarrolladores, si se podría afirmar que es una métrica confiable del tamaño real del proyecto. Habitualmente es una métrica que combinamos con índices como la Complejidad Ciclomática o el número de clases.

En el mundo de Visual Studio contamos con una funcionalidad denominada Code Metrics la cual nos muestra una serie de estadísticas muy útiles sobre nuestra solución y sus proyectos. Entre estas métricas está la cuenta de líneas de código.

Metrics

Ahora, esta cuenta no es exactamente todas las líneas de código de nuestra solución (o proyecto), sino una cuenta aproximada basada en el código IL generado por la compilación (vamos, más o menos lo que hace la NASA). Éste índice como tal es bastante útil en determinar si un método está haciendo más de lo que debe, pues aún cuando un método tenga cientos de líneas de código físico, la compilación puede hacer que el número de líneas sea radicalmente menos, y si el mismo no baja, puede indicar una pobre o inapropiada mantenibilidad del método.

Cuando necesito conocer las líneas reales de código fuente, su tipo (código, comentario o línea en blanco) y su distribución (SQL, XML, ASP.NET o C#) empleo y recomiendo un pequeño programa llamado CLOC, el cual no requiere instalacieon, es súper fácil de usar y soporta cientos de tecnologías y lenguajes de programación, incluyendo C#, ASP.NET, VB.NET y muchos más.

Count

Entre sus características básicas, me es muy útil que es capaz de reconocer el número de líneas blancas dentro de código, y aquellas que se corresponden a texto de documentación y comentarios, con lo cual se puede obtener métricas, ahora si de la calidad del código, sobre cuanto documentado este puede estar. Por ejemplo, imaginen un código fuente sin ninguna documentación. A través de una aplicación como CLOC podríamos determinar esto y así levantar la correspondiente incidencia.

Otras característica interesante es que se puede obtener un reporte de líneas de código por archivo o por lenguaje.

El reporte puede exportarse a un archivo separado por comas, a XML e incluso en sentencias de SQL que pueden ser útiles para inyectar en una base de datos de reportes de estado y salud del proyecto.

Profesionalmente, al realizar auditorias técnicas complemento el resultado del Code Metrics del Visual Studio con los resultados de CLOC. De esa forma puedo ofrecer una fotografía completa de la mantenibilidad del código versus cuanto está escrito, cuanto es espacio vacío y que tanta documentación podríamos encontrar.

Anuncios

Referencias de Proyectos Dependientes del Modo de Compilación

Una de las cosas que más me gustan de la plataforma .NET y del Visual Studio es la gestión de referencias a otras librerías o entre proyectos dentro de una misma solución. Y ni de que hablar que desde que vio a la luz la plataforma NuGet, el tema de gestión y distribución de estas librerías se ha convertido en todo un paseo por el campo, ya que no es necesario indicar que librerías tiene que tener instalado o descargado los desarrolladores, sino que la solución (apropiadamente configurada) se encargará de recuperar los archivos correspondientes durante la primera compilación de forma automática… toda una maravilla!

Sin embargo, y en particular, al trabajar con librerías de terceras partes o third party libraries que no estén disponibles a través de NuGet, existen diversas estrategias que ya se empleaban desde los principios de .NET. La que personal y profesionalmente considero la más apropiada es la de crear una carpeta Lib en la raíz del directorio que contiene al archivo de solución (.sln) e ir colocando allí los diferentes archivos dll que se van a emplear.

Referencias de Proyectos Dependientes del Modo de Compilación - File System

Ejemplo de un proyecto pequeño y sencillo en .NET con soporte para NuGet. Nótese la carpeta Lib en la raíz del directorio que contiene el archivo de solución (.sln).

La parte final sería agregar el contenido de la carpeta Lib a la solución (a través de las funcionalidades de Add solution’s folder y de Add existing item…) para poder gestionar estas referencias a través del repositorio de versiones que empleemos: TFS, GitHub, SVN, o cualquier otro.

Pero… ¿Qué pasa si la version de librería de terceros que necesitamos emplear debe ser diferente en tiempo de desarrollo que en tiempo de despliegue/producción? ¿Cómo se gestiona una referencia a dos archivos dll diferentes dentro de un mismo proyecto y solución?

En principio no se puede. La referencia es exclusive a un archivo dll y a una versión de éste en específico, lo cual plantea un inconveniente que la mayoría de las personas termina resolviendo a través de dudosas prácticas y aún peores estrategias. Otros directamente culpan al Visual Studio y alegan que en otros IDEs es fácil hacer algo como ésto.

El problema es que esta necesidad es tan poco común que pocas personas en una empresa saben como resolverlo, pero la verdad es que es muy fácil a través del Visual Studio y de MSBUILD.

Solución

Supongamos que necesitamos referenciar una lirabría llamada My.Special.Library en una version especifica cunado compilamos en DEBUG y en una versión diferente cuando compilamos en RELEASE. Los pasos serían los siguientes:

  1. Desde el sistema de archivos navegamos hasta el directorio de la solución, y buscamos el directorio especial Lib.
  2. Dentro de Lib creamos dos (2) nuevos directorios: Debug y Release.
  3. Colocamos en cada nuevo directorio la versión de la libraría de terceros (en nuestro ejemplo My.Special.Library) donde corresponda, tal que la versión que queremos para el modo DEBUG esté en el directorio Debug, y de igual manera para el caso del modo RELEASE.
  4. Es importante desde el Visual Studio crear dos (2) ‘Solution Folders’ dentro del ‘Solution Folder’ de Lib para mapear los directorios Debug y Release así como su contenido para poder gestionarlos a través de nuestro correspondiente sistema de gestión de versiones.
    Referencias de Proyectos Dependientes del Modo de Compilación - Lib
  5. Luego, desde el Visual Studio, seleccionamos el proyecto que queremos trabajar desde el ‘Solutio Explorer’.
  6. Hacemos right-click sobre el proyecto y elegimos la opción de ‘Unload project’.
  7. Una vez que el proyecto a sido descargado, hacemos nuevamente right-click y elegimos la opción de editar el archivo .csproj.
  8. Se nos mostrará el XML que conforma el MSBUILD del proyecto. Allí debemos buscar donde comienzan las inclusions de las referencias en el proyecto, lo cual es fácil de identificar porque son una sucesión de tags Reference.
  9. Debemos colocar la siguientes instrucciones (XML) justo antes del ItemGroup que contiene las referencias:
    <Choose>
        <When Condition="'$(Configuration)'=='Debug'">
          <ItemGroup>
            <Reference Include="My.Special.Library, Version=1.222.2033.0, Culture=neutral, PublicKeyToken=26e441566366f4ab">
               <SpecificVersion>False</SpecificVersion>
              <HintPath>..\Lib\Debug\My.Special.Library.dll</HintPath>
            </Reference>
          </ItemGroup>
        </When>
         <Otherwise>
          <ItemGroup>
            <Reference Include="My.Special.Library, Version=1.342.2035.0, Culture=neutral, PublicKeyToken=26e441566366f4ab">
              <SpecificVersion>False</SpecificVersion>
               <HintPath>..\Lib\Release\My.Special.Library.dll</HintPath>
            </Reference>
          </ItemGroup>
        </Otherwise>
    </Choose> 
    
  10. Salvamos el archivo .csproj.
  11. Hacemos de nuevo right-click sobre el proyecto desde el ‘Solution Explorer’ y ahora elegimos la opción de ‘Reload Project’. Si el Visual Studio nos pregunta para cerrar el archivo .csproj, le contestamos que si.

Y listo, ya tenemos configurado el proyecto para que considerere una versión u otra de la librería de terceros dependiendo del modo de compilación, sin tener que hacer nada más, todo de forma automática y transparente.

Lo más interesante de esta solución es que la referencia se actualiza automáticamente y sin interrupciones en el proyecto que la contenga cuando cambiamos de modos de compilación desde el Visual Studio. Así, si estamos en el modo DEBUG, se estará referenciando a la version 1.222.2033.0, pero si cambiamos a otro modo (digamos RELEASE) automáticamente y sin enterarnos se actualizará la referencia del proyecto para utilizer la version 1.342.2035.0.

Por otro lado, no es estrictamente necesario tener que realizar la selección de una versián u otra si estamos en modo DEBUG o no, ya que como Visual Studio y el compilador de C# soportan variables de compilación, podemos definir nuestras porpias variables y emplear éstas para determiner que libraría y versión referenciar en nuestros proyectos.

Espero que este truco les sea de utilidad.

Como Insertar más de 185.000 Registros con Entity Framework en Menos de Dos Milisegundos… Like a Boss!!!

En los últimos meses he tenido la dicha de trabajar con la tecnología Entity Framework del ADO.NET de Microsoft. Debo decir que el paradigma de Code-First es una maravilla… es adictivo y te hace sentir en total control del modo en que se almacenan los datos del sistema que se esté diseñando/implementando.

Sin embargo tiene una limitante importante: cuando hay que sembrar la base de datos (Seed) con información para tablas maestras, y dicha información tiene un volumen importante de registros o datos, entonces tanto el mismísimo Visual Studio como el Entity Framework fallan. En mi caso, fue con intentar sembrar todos los códigos postales de España y Portugal… un poco más de 185.000 registros. El problema es esencialmente uno sólo: el número de registros a insertar es muy alto.

Kudos

Antes de empezar presentando la solución a este problema, me gustaria mencionar que el título de esta publicación es cortesía de mi esposa: Astrid a quien podéis seguir a través de Gikvanna en WordPress, Twitter y Facebook.

El Problema

Por un lado, con Code-First, debemos crear instancias de clases POCO que queremos insertar, pero cuando se tiene demasiados elementos, el Visual Studio se relentiza debido a que tiene que mantener constancia, integridad y estilo de los elementos que escribimos. No importa cuanta memoria o capacidad de procesamiento tenga nuestro equipo de trabajo… el Visual Studio se pondrá denso y pesado.

    /// <summary>
    /// Permite aplicar configuraciones a una base de datos.
    /// </summary>
    internal class DatabaseInitializer : IDatabaseInitializer<DatabaseContext>
    {
        ...
        /// <summary>
        /// Agrega datos al contexto (<see cref="DatabaseContext"/>) para <i>sembrarlo</i>.
        /// </summary>
        /// <param name="context">El contexto a sembrar.</param>
        protected override void Seed(DatabaseContext context)
        {
            ...
            // Códigos postales para el 'seed'.
            List<PostalCode> = new List<PostalCode>
            {
                // Más de 185.000 registros de esta misma forma.
                new PostalCode { Value="pc1", AlternativeId="cp1" },
                new PostalCode { Value="pc2", AlternativeId="cp2" },
                new PostalCode { Value="pc3", AlternativeId="cp3" },
                new PostalCode { Value="pc4", AlternativeId="cp4" },
                new PostalCode { Value="pc5", AlternativeId="cp5" },
                new PostalCode { Value="pc6", AlternativeId="cp6" },
                ...
            }
            ...
        }
        ...
    }

Por otro lado cuando intentemos iniciar la aplicación por primera vez (sin base de datos alguna), el tiempo de creación de ésta junto al sembrado es tan alto y el número de INSERTs tan elevado que se producen muchos time-outs (tanto en la base de datos como en el servidor web en caso de aplicaciones de este tipo); lo cual hace el proceso de desarrollo e implementación muy frustrante.

Una Alternativa de Solución

El primer problema, referente al número elevado de instancias de la clase POCO que debemos crear para el sembrado, se soluciona fácilmente almacenando los datos en archivos de texto o de recursos en un formato parseable, como por ejemplo un listado de elementos separados por coma.

        private static IList<PostalCode> ReadPostalCodes()
        {
            // Leer los códigos postales desde recursos 'embebidos' (empotrados) en el ensamblado (assembly).
            Assembly currentAssembly = Assembly.GetExecutingAssembly();
            IEnumerable<string> postalCodesResourceFileNames = currentAssembly.GetManifestResourceNames().Where(resourceName => resourceName.Contains("PostalCodes_"));

            Stream stream = null;
            string[] itemTokens = null;

            IList<PostalCode> postalCodes = new List<PostalCode>();

            foreach (string postalCodeResourceFileName in postalCodesResourceFileNames)
            {
                try
                {
                    stream = currentAssembly.GetManifestResourceStream(postalCodeResourceFileName);

                    using (StreamReader streamReader = new StreamReader(stream))
                    {
                        // Cada dato está separado por un salto de línea, y a su vez, cada elemento de cada dato está separado por comas.
                        IEnumerable<string> postalCodeItems = streamReader.ReadToEnd().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

                        foreach (string postalCodeItem in postalCodeItems)
                        {
                            itemTokens = postalCodeItem.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                            postalCodes.Add(new PostalCode { Country = itemTokens[0], Value = itemTokens[1] });
                        }
                    }
                }
                finally
                {
                    if (stream != null)
                    {
                        stream.Dispose();
                    }
                }
            }

            return postalCodes;
        }

Para resolver el segundo problema, referente a la inserción de un elevado número de registros, nos apoyaremos en una clase del Framework .NET llamada SqlBulkCopy, la cual toma un DataTable o un IDataReader para realizar una inserción masiva de datos en la base de datos.

En los siguientes códigos fuentes, los comentarios estarán en inglés tal y como los tengo en mis proyectos, y que por diversas razones no he tenido tiempo de traducirlos al Castellano. Eventualmente trataré de ir editando esta publicación para traducirlas poco a poco.

La idea es entonces integrar la clase DbContext empleada en Entity Framework para interactuar con el repositorio de datos a través la clase SqlBulkCopy. Esta integración es fácilmente alcanzable con métodos de extensión que permitan emplear el método de inserción con cualquier DbContext; por lo cual el primer paso es crear la clase estática (con los respectivos using que necesitaremos a lo largo de la solución).

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Reflection;

namespace My.Data.Entity
{
    /// <summary>
    /// Contains extensions method for <see cref="DbContext"/> objects that works specifically with Microsoft SQL Server databases.
    /// </summary>
    public static class DbContextSqlServerExtensions
    {
        ...
    }
}

Dentro de esta clase crearemos el método de extensión BulkInsert para inserción masiva, junto con una sobre carga cuyo objetivo explicaré más adelante. Como podréis ver, el método carece prácticamente de toda lógica más allá de invocar a otro método llamado ExecuteBulkInsert el cual será el verdadero responsable de realizar la lógica de inserción masiva apoyado en la clase SqlBulkCopy.

Sin embargo, igual en la documentación hacemos incapié en una advertencia muy importante que también debemos tomar en cuenta: en esta solución se emplea Reflection para acceder a métodos y propiedades de las clases de Entity Framework que para la versión actual (no beta) disponible a la fecha de esta publicación, correspondiente a la 5.0, son internal y no públicas (public) en el API. Esto significa que esta solución puede dejar de funcionar, o no funcionar como se espera cuando se emplee con nuevas versiones de Entity Framework que puedan haber modificado el comportamiento sistémico de las propiedades y métodos invocados a través de Reflection.


        /// <summary>
        /// <para>
        /// Performs a bulk load of a collection of entities into a database, and regenerates the entity list with updated 
        /// information.
        /// </para>
        /// <para>
        /// Due to some technological limitations, the regeneration of the list is actually a retrieval of fresh data from
        /// storage (database) with all the available entities, in other words, it will get all the entities from storage
        /// and not only those in the original entity list.
        /// </para>
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method leverages the <see cref="SqlBulkCopy "/> class and the <c>BCP</c> protocol to insert a collection
        /// of entities into the storage configured in the <see cref="DbContext"/> parameter.
        /// </para>
        /// <para>
        /// <b><u>WARNING:</u></b> This method relays on <a href="http://msdn.microsoft.com/en-us/library/ms173183(v=vs.100).aspx"><c>Reflection</c></a> 
        /// to retrieve values from properties that are not <c>public</c> (in fact, properties that are  <c>internal</c> or <c>private</c>). 
        /// This means that there is a possible risk that this method will break, stop working or not-working as expected when new versions of 
        /// Entity Framework are released by Microsoft.
        /// </para>
        /// </remarks>
        /// <typeparam name="TEntity">The type of entity to insert.</typeparam>
        /// <param name="context">
        /// The context to work with. This context must have configured a connection to a storage (like a database).
        /// </param>
        /// <param name="list">
        /// A collection of entities to insert.This reference might be reloaded it with updated entities
        /// if the <paramref name="reloadEntities"/> parameter is set to <c>true</c>.
        /// </param>
        /// <param name="reloadEntities">
        /// Determines if the entities list must be reloaded with updated entities from storage. If <c>true</c> the entities are 
        /// going to be reloaded and stored in the list passed as parameter, otherwise <c>false</c> and the entities are not going
        /// to be reloaded.
        /// </param>
        public static void BulkInsert<TEntity>(this DbContext context, IList<TEntity> list, bool reloadEntities) where TEntity : class
        {
            ExecuteBulkInsert<TEntity>(context, ref list, reloadEntities);
        }

Como puede apreciarse, el método BulkInsert posee dos (2) sobre cargas: la primera simplemente considera una colección (en forma de lista) de las entidades (objetos de las clases POCO) a insertar mientras que la segunda toma un valor booleano que corresponde a un indicador de si deseamos recargar las entidades una vez insertadas, lo cual puede resultar muy útil para obtener nuevamente estas entidades pero con las propiedades correspondientes a las claves primaras en el repositorio de datos apropiadamente establecidas (de ahí que el método ExecuteBulkInsert reciba la colección/lista de entidades como un parámetro ref).

        /// <summary>
        /// Actually executes the bulk load of a collection of entities into a database, and regenerates the entity list with updated 
        /// information, if required.
        /// </summary>
        /// <typeparam name="TEntity">The type of entity to insert.</typeparam>
        /// <param name="context">
        /// The context to work with. This context must have configured a connection to a storage (like a database).
        /// </param>
        /// <param name="list">
        /// A collection of entities to insert.This reference might be reloaded it with updated entities if 
        /// the <paramref name="reloadEntities"/> parameter is set to <c>true</c>. It is received as <c>ref</c>
        /// in order to warranty the reload.
        /// </param>
        /// <param name="reloadEntities">
        /// Determines if the entities list must be reloaded with updated entities from storage. If <c>true</c> the entities are 
        /// going to be reloaded and stored in the list passed as parameter, otherwise <c>false</c> and the entities are not going
        /// to be reloaded.
        /// </param>
        private static void ExecuteBulkInsert<TEntity>(DbContext context, ref IList<TEntity> list, bool reloadEntities) where TEntity : class
        {
            Type typeOfEntity = typeof(TEntity);
            ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;

            // Retrieve the Storage Data Space for the entity we are working with. From it, get the members which represents the columns in the database. From 
            // those members, we can get the real name of the columns (since the developer may have changed them with the 'HasColumnName' method) and their types.            
            ReadOnlyMetadataCollection<EdmMember> storageSpaceMembers = objectContext.MetadataWorkspace.GetItem<EntityType>(@"CodeFirstDatabaseSchema." + typeOfEntity.Name, DataSpace.SSpace).Members;

            using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(context.Database.Connection.ConnectionString))
            {
                using (DataTable dataTable = new DataTable())
                {
                    dataTable.Locale = CultureInfo.InvariantCulture;

                    // For each column defined in the Storage Data Space...
                    for (int index = 0; index < storageSpaceMembers.Count; index++)
                    {
                        // ...configure the instance of the SqlBulkCopy class with the name of the source and destination column...
                        sqlBulkCopy.ColumnMappings.Add(storageSpaceMembers[index].Name, storageSpaceMembers[index].Name);

                        // ...and also configure a DataTable with the name of the column and its respective CLR type.
                        /**************************************************************************************************************
                         * This is the risky part of the code. From this point on, the properties and methods employed are invoked by *
                         * Reflection and are non-public (i.e. internal) in the Entity Framework assembly.                            *
                         **************************************************************************************************************/
                        /***************************************************************************************************************** 
                         * WARNING: the 'ClrType' property is not public; actually, it is 'internal virtual'. This means that there is a *
                         * possible risk that this  method will break, stop working or not working as expected when new versions of      *
                         * Entity Framework are released by Microsoft.                                                                   *
                         *****************************************************************************************************************/
                        dataTable.Columns.Add(storageSpaceMembers[index].Name, GetPropertyValue<Type>(storageSpaceMembers[index].TypeUsage.EdmType, @"ClrType"));
                    }

                    PopulateDataTable(dataTable, objectContext, list, storageSpaceMembers);

                    sqlBulkCopy.DestinationTableName = GetTableName(objectContext, typeOfEntity);
                    sqlBulkCopy.BatchSize = list.Count;
                    sqlBulkCopy.WriteToServer(dataTable);
                }
            }

            if (reloadEntities)
            {
                list = context.Set<TEntity>().ToList();
            }
        }

El primer paso consiste en obtener el nombre real de las columnas que se corresponden a las propiedades de la entidad que deseamos insertar, el cual en principio no debería diferir mucho de dichas propiedades, pero que ciertamente puede haber cambiado si el programador ha empleado el método HasColumnName al configurar como se almacenaría la entidad en el repositorio de datos.

Para obtener los nombres de las columnas, se realiza una búsqueda en la metadata del contexto obteniendo de ésta todos los objetos que existan en el modelo de almacenamiento cuyo nombre se corresponda al nombre de la entidad a insertar precedido del nombre del esquema por defecto para desarrollos con Code First: CodeFirstDatabaseSchema.

Con los nombres de las columnas se puede crear la instancia de la clase SqlBulkCopy a usar, configurando ésta con los nombres de la columna origen y la columna destino. Así mismo, se crea y configura una instancia de la clase DataTable que se empleará con el SqlBulkCopy para realizar la inserción. Sin embargo, el DataTable requiere conocer el tipo de dato exacto y concreto de la columna, para lo cual es necesario invocar al método internal virtual ClrType de la clase EdmMember obtenida del modelo de almacenamiento de la metadata a través de Reflection.

Una vez configurada la instancia del DataTable es necesario poblarla con los valores apropiados, para lo cual empleamos el método auxiliar PopulateDataTable.

        /// <summary>
        /// Fills a <see cref="DataTable" /> with data from a list of entities.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method is an auxiliary method for the <see cref="DbContextExtensions.BulkInsert{TEnity}(DbContext, ref IList{TEnity}, bool)"/>  method.
        /// </para>
        /// <para>
        /// The <c>storageSpaceMembers</c> parameter is just a performance consideration. It is previously retrieved in the 
        /// <see cref="DbContextExtensions.BulkInsert{TEnity}(DbContext, ref IList{TEnity}, bool)"/> method, and in order to not calculate it again, it is passed as
        /// parameter to this method. However, this can be changed if improvement of the isolation of this method is required.
        /// </para>
        /// </remarks>
        /// <typeparam name="TEnity">The type of entity used to fill the <see cref="DataTable"/>.</typeparam>
        /// <param name="dataTable">The <see cref="DataTable"/> to fill.</param>
        /// <param name="objectContext">A context to perform operation on the entities. </param>
        /// <param name="list">The list of entities used to fill the <see cref="DataTable"/>.</param>
        /// <param name="storageSpaceMembers">A list of members for the entity type on the Storage Data Space.</param>
        private static void PopulateDataTable<TEnity>(DataTable dataTable, ObjectContext objectContext, IList<TEnity> list, ReadOnlyMetadataCollection<EdmMember> storageSpaceMembers) where TEnity : class
        {
            Type typeOfEntity = typeof(TEnity);

            // Get the Object Space, which contains data and information of the current model (classes and members).
            ReadOnlyMetadataCollection<EdmMember> objectSpaceMembers = objectContext.MetadataWorkspace.GetItem<EntityType>(typeOfEntity.FullName, true, DataSpace.OSpace).Members;
            EntitySetBase entitySetBase = objectContext.MetadataWorkspace.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace).BaseEntitySets.FirstOrDefault(baseEntitySet => baseEntitySet.ElementType.Name.Equals(typeOfEntity.Name, StringComparison.OrdinalIgnoreCase));
            EntityType entityType = (EntityType)entitySetBase.ElementType;

            foreach (TEnity entity in list)
            {
                DataRow dataRow = dataTable.NewRow();

                // For each property of the entity, retrieve its value and assign it to the appropriate corresponding column of the DataTable.
                foreach (EdmProperty edmProperty in entityType.Properties)
                {
                    // To obtain the appropriate column, We use a combination of the Storage Data Space with the Object Space.
                    // - First: obtain from the Object Space the property that matches the name of the entity property being populated in the DataTable.
                    // - Second: get the index of the property from the Object Space. This index or position in the Object Space is going to be the same 
                    //           in all other spaces, including the Storage Data Space.
                    // - Third: with this index, retrieve the corresponding property in the Storage Data Space.
                    // - Fourth: from the retrieved property in the Storage Data Space, get its name which will correspond to the column name in the DataTable.
                    // - Fifth: by 'Reflection' get the value of the property and assign it to the DataTable.
                    dataRow[storageSpaceMembers[objectSpaceMembers.IndexOf(objectSpaceMembers[edmProperty.Name])].Name] = GetPropertyValue<object>(entity, edmProperty.Name);
                }

                // Get the appropriate value of any referencial constraint (like a foreign key).
                if (entityType.NavigationProperties.Count > 0)
                {
                    ICollection<ReferentialConstraint> referentialConstraints = GetReferentialConstraints(objectContext, typeOfEntity, entitySetBase);

                    foreach (NavigationProperty navigationProperty in entityType.NavigationProperties)
                    {
                        // Filter the referencial contraints to get only those that apply to the entity being manipulated.
                        // Only the "From Role" part of the relationship is required since We are inserting the "To Role" part.
                        IEnumerable<ReferentialConstraint> filteredReferentialConstraints = referentialConstraints.Where(referentialConstraint => referentialConstraint.FromRole.Name.Equals(navigationProperty.Name, StringComparison.OrdinalIgnoreCase));

                        foreach (ReferentialConstraint referentialConstraint in filteredReferentialConstraints)
                        {
                            dataRow[referentialConstraint.ToProperties[0].Name] = GetPropertyValue<object>(GetPropertyValue<object>(entity, referentialConstraint.FromRole.Name), referentialConstraint.FromProperties[0].Name);
                        }
                    }
                }

                dataTable.Rows.Add(dataRow);
            }
        }

Este método se encargará de combinar la información del espacio del modelo de almacenamiento con la información del espacio del modelo de objetos de la metadata del contexto para mapear cada propiedad con la columna correspondiente y asignar el valor respectivo. El truco aquí es darse cuenta que la indexación entre los diferentes espacios de la metadata del contexto es la misma; así, un elemento en una específica posición del espacio de almacenamiento corresponderá al mismo elemento en la misma específica posición en cualquiera de los otros espacios (conceptual, de objectos, etc.).

Un aspecto importante de éste método es la determinación de las referencias (esto es, claves foráneas o secundarias) para obtener los valores que correspondan únicamente a la entidad siendo manipulada y evitar problemas de congruencia en los datos a insertar o excepciones de Entity Framework debido a éstas. Por cada elemento de restricción referencial, se busca la propiedad que le corresponde y por tanto la columna respectiva en el DataTable para asignar su valor.

Estas restricciones referenciales se determinan a través de una invocación al método auxiliar GetReferentialConstraints, el cual toma el contexto de datos siendo empleado, el tipo de la entidad (esto es, el tipo del POCO) y un objeto denominado entity set base que se corresponde a la metadata que relaciona el modelo de almacenamiento con el modelo conceptual (del cual se extraen las referencias).

        /// <summary>
        /// Retrieves a collection of <see cref="ReferentialConstraint"/> elements for a given entity type (and entity set base)
        /// on the current object context.
        /// </summary>
        /// <remarks>
        /// <b><u>WARNING:</u></b> This method relays heavily on <a href="http://msdn.microsoft.com/en-us/library/ms173183(v=vs.100).aspx"><c>Reflection</c></a> 
        /// to retrieve the referential constraints information because it is hidden in the Entity Framework assembly (i.e. <c>internal</c> types and members). 
        /// Starting with the retrieval of the mapping between the conceptual model and the storage model (<see cref="DataSpace.CSSpace"/>) everything in the 
        /// obtained <see cref="ItemCollection"/> is non-public. This means that there is a possible risk that this method will break, stop working or 
        /// not-working as expected when new versions of Entity Framework are released by Microsoft.
        /// </remarks>
        /// <param name="objectContext">Object context to work with.</param>
        /// <param name="entityType">Type of the entity whose referential constraints are going to be retrieved.</param>
        /// <param name="entitySetBase">Entity base with the information of referential constraints to retrieve.</param>
        /// <returns>A collection of referential constraints for the given entity in the current object context.</returns>
        private static ICollection<ReferentialConstraint> GetReferentialConstraints(ObjectContext objectContext, Type entityType, EntitySetBase entitySetBase)
        {
            List<ReferentialConstraint> referentialConstraints = new List<ReferentialConstraint>();

            // Get the data space that contains the mapping between the conceptual model and the storage model.
            GlobalItem conceptToStorageItemCollection = objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace).First();

            /**************************************************************************************************************
             * This is the risky part of the code. From this point on, the properties and methods employed are invoked by *
             * Reflection and are non-public (i.e. internal) in the Entity Framework assembly.                            *
             **************************************************************************************************************/

            // - First: get the entity set mapping (System.Data.Mapping.StorageSetMapping class) from the data space that maps
            //          the conceptual model and the storage model by invoking the internal 'GetEntitySetMapping' method.
            //
            // - Second: get the collection of type mappings (a System.Collections.ObjectModel.ReadOnlyCollection of System.Data.Mapping.StorageSetMapping objects) 
            //           from the entity set mapping with the 'TypeMappings' property. Since the collection elements is internal it will be 'unknow'
            //           to the compiler during design time, so it's marked as dynamic.
            dynamic dynamicStorageTypeMappings = GetPropertyValue<dynamic>(conceptToStorageItemCollection.GetType().GetMethod("GetEntitySetMapping", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(conceptToStorageItemCollection, new object[] { entitySetBase.Name }), "TypeMappings");

            foreach (object storageTypeMapping in dynamicStorageTypeMappings)
            {
                // - Third: for each type mapping, get the mapping fragments (a System.Collections.ObjectModel.ReadOnlyCollection of System.Data.Mapping.StorageMappingFragment
                //          objects). Each mapping fragment provides information and data for those properties of a type that map to a single table.
                dynamic dynamicStorageMappingFragments = GetPropertyValue<dynamic>(storageTypeMapping, "MappingFragments");

                foreach (object storageMappingFragment in dynamicStorageMappingFragments)
                {
                    // - Fourth: get an entity set that represents a table from which the properties are mapped in this fragment by invoking the 'TableSet' property.
                    EntitySet tableSet = GetPropertyValue<EntitySet>(storageMappingFragment, "TableSet");

                    // Fifth: just verify the table set is not empty and its name corresponds to the entity we are working with.
                    if (tableSet != null && tableSet.Name.Equals(entityType.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        // - Sixth: get a collection of tuples of assosiation sets and referencial contraints from the table set by invoking the internal 
                        //          property 'ForeignKeyDependents'.
                        //
                        // - Seventh: from such collection, get only the referencial contraints items and add them to the collection we are going to 
                        //            return as result of this method.
                        referentialConstraints.AddRange(GetPropertyValue<ICollection<Tuple<AssociationSet, ReferentialConstraint>>>(tableSet, "ForeignKeyDependents").Select(foreignKeyDependent => foreignKeyDependent.Item2));
                    }
                }
            }

            return referentialConstraints;
        }

Este método es el más delicado de todos, ya que es el que más propiedades y métodos private o internal del Entity Framework invoca a través de Reflection. Básicamente, todo lo que se invoca tras obtener la información de la metadata que relaciona el modelo conceptual con el modelo de almacenamiento es o bien internal o bien private. Por otro lado, otro reto es que los tipos del mapeo entre el modelo conceptual y el de almacenamiento no son conocidos, por lo cual deben ser tratados como dynamic. Lo que se hace con estos objetos es buscar aquellos que tienen relación con la entidad que se está insertando para identificarlos como referencias con ésta y conservarlas en una colección que retornaremos al llamador responsable de poblar el DataTable.

Finalmente, nuevamente en el método ExecuteBulkInsert se establece el nombre de la tabla (entidad) a poblar con el SqlBulkCopy, el número de registros y los registros en si.

¡Y ESO SERÍA TODO!

Al ejecutar mi solución sin la base de datos, el proceso de crearla y poblarla (sembrarla) con los códigos postales de España y Portugal (que como hemos mencionado es un número superior a 185.000 registros) tarda algo menos de… ¡¡¡dos milisegundos!!! No hay time/outs ni fallos de ningún tipo.

Más detalles de la implementación en los comentarios de los propios métodos que he dejado como parte del código fuente expuesto en esta publicación. Se que están en inglés, pero los tengo así por temas de mi trabajo del día a día. Sin embargo espero que podáis entenderlos y os aportén más luz sobre esta alternativa de solución.

Métodos Auxiliares

A lo largo de esta publicación, habrán visto referencias e invocaciones a métodos como GetPropertyValue o GetTableName. Dichos métodos no existen en el API del Framework .NET o del Entity Framework. Son métodos auxiliares que he creado para conseguir el objetivo de esta solución. En esta parte os dejo el código fuente de los mismos.

        /// <summary>
        /// Retrieves a the named property (public or not) from an object trough <c>Reflection</c>.
        /// </summary>
        /// <typeparam name="TValue">The type of the property to retrieve.</typeparam>
        /// <param name="element">The object where to reflect the property.</param>
        /// <param name="propertyName">The name of the property to retrieve.</param>
        /// <returns>The value of the named property.</returns>
        private static TValue GetPropertyValue<TValue>(object element, string propertyName) where TValue : class
        {
            return element.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).GetValue(element, null) as TValue;
        }

        /// <summary>
        /// Retrieves the name of the dataTable.
        /// </summary>
        /// <remarks>
        /// Developers may change the name of the dataTable from the expected standard to a custom one by using 
        /// the <see cref="System.Data.Entity.ModelConfiguration.EntityTypeConfiguration{TEntityType}.ToTable(string)"/> method. This method will retrieve such configuration.
        /// </remarks>
        /// <param name="objectContext">The <see cref="ObjectContext"/> with mapping and concept model data.</param>
        /// <param name="entityType">Type of the entity type configuration whose dataTable name is going to be retrieved.</param>
        /// <returns>The configured name of the dataTable for the provided entity type configuration.</returns>
        private static string GetTableName(ObjectContext objectContext, Type entityType)
        {
            return objectContext.MetadataWorkspace.GetItems<EntityContainer>(DataSpace.SSpace).FirstOrDefault().BaseEntitySets.FirstOrDefault(entityBaseSet => entityBaseSet.Name.Equals(entityType.Name, StringComparison.OrdinalIgnoreCase)).MetadataProperties[@"Table"].Value.ToString();
        }

Otros Gestores de Bases de Datos

El Entity Framework soporta cualquier gestor de base de datos que implemente un conector para ADO.NET. Para el caso de esta solución y el uso de la clase SqlBulkCopy he asumido que se está usando Entity Framework con una base de datos Microsoft SQL Server. Sin embargo, si se quisiera usar un gestor como ORACLE, en vez de usar la clase SqlBulkCopy se tendría que usar las facilidades propias del ODP.NET como el Bulk Array (también conocido como ODP.NET Array Binding).

Como Validar el DNI o el RUC de Perú en C#

En estos días me ha tocado trabajar en un proyecto que, como tantos otros hoy día, está pensado para un entorno empresarial globalizado.

Parte de las reglas de negocio del proyecto exige, como también es común en tantos otros proyectos, validar el documento de identidad correspondiente al cliente para el país al cual éste pertenece. Para el caso de países como España o Brasil existe amplia documentación y recursos referentes al algoritmo empleado para validar sus respectivos documentos de identidad.

Pero lamentablemente para Perú, uno de los países que tengo que gestionar, la cantidad y la calidad de la información disponible es decepcionante.

DNI Electrónico Peruano

Por otro lado, al hablar de documentos de identidad en Perú, debemos distinguir entre el DNI (Documento Único de Identidad) y el RUC (Registro Único de Contribuyente). El primero aplica a los ciudadanos en general y cuenta con un identificador conocido como CUI (Cédula Única de Identidad), mientras que el segundo aplica a empresas y comercios.

El siguiente algoritmo permite validar documentos de identidad peruanos (tanto DNI como RUC). Para el caso del DNI, es independiente que el CUI tenga como término de verificación un número o una letra.

public static bool ValidateIdentificationDocumentPeru(string identificationDocument)
{
    if (!string.IsNullOrEmpty(identificationDocument))
    {
        int addition = 0;
        int[] hash = { 5, 4, 3, 2, 7, 6, 5, 4, 3, 2 };
        int identificationDocumentLength = identificationDocument.Length;

        string identificationComponent = identificationDocument.Substring(0, identificationDocumentLength - 1);

        int identificationComponentLength = identificationComponent.Length;

        int diff = hash.Length - identificationComponentLength;

        for (int i = identificationComponentLength - 1; i >= 0; i--)
        {
            addition += (identificationComponent[i] - '0') * hash[i + diff];
        }

        addition = 11 - (addition % 11);

        if (addition == 11)
        {
            addition = 0;
        }

        char last = char.ToUpperInvariant(identificationDocument[identificationDocumentLength - 1]);

        if (identificationDocumentLength == 11)
        {
            // The identification document corresponds to a RUC.
            return addition.Equals(last - '0');
        }
        else if (char.IsDigit(last))
        {
            // The identification document corresponds to a DNI with a number as verification digit.
            char[] hashNumbers = { '6', '7', '8', '9', '0', '1', '1', '2', '3', '4', '5' };
            return last.Equals(hashNumbers[addition]);
        }
        else if (char.IsLetter(last))
        {
            // The identification document corresponds to a DNI with a letter as verification digit.
            char[] hashLetters = { 'K', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' };
            return last.Equals(hashLetters[addition]);
        }
    }

    return false;
}

Finalmente, el gobierno de Perú pone a disposición la siguiente herramienta para verificar documentos de identidad; y esta otra herramienta para validar RUC.

Espero que les sea de utilidad.

Un Framework para Programación Orientada a Aspectos (AOP) en C#    Parte 3: Un aspecto para manejo de excepciones

Esta publicación es la cuarta parte (aunque diga parte tres) de una serie de cuatro publicaciones que estaré realizando sobre programación orientada a aspectos en .NET empleando el lenguaje de programación C#.

A parte del registro de eventos, otro elemento muy común en la programación orientada a objetos y en los lenguajes actuales (como Java o C#) es la gestión de excepciones.

El otro típico infierno al que se enfrenta un arquitecto o líder técnico a la hora de establecer la política de gestión de errores y excepciones en su diseño es la gobernabilidad de dicho diseño, y la monitorización de su cumplimiento por parte del equipo de desarrollo, cosa que se torna excesivamente complicada por la creatividad de algunos miembros del equipo que rompe la homogeneidad del mecanismo elegido como parte del modelo. Hasta aquí, este párrafo es prácticamente igual al de la segunda parte de esta serie de artículos.

Y es que el diseño y la gobernabilidad del mecanismo de gestión y manejo de excepciones adoloce de los mismos problemas y retos que que podemos encontrar con el registro de eventos.

Particularmente se encuentra que los dos (2) aspectos más difíciles de gestionar son el acto de capturar y apropiadamente manejar la excepción, seguidamente de la política a aplicar sobre ésta. Muchas veces podemos encontrarnos con aplicativos web que cuando una excepción ocurre nos muestra la temida pantalla amarilla de ASP.NET o directamente nos expone todo el stack trace de la excepción (con información de base de datos y todo), lo cual fácilmente podría usar un potencial atacante para literalmente destruir el aplicativo.

ASP.NET Yellow Page of Death

A través del enfoque de AOP, es posible interceptar las llamadas a los métodos y determinar cuando estos han generado una excepción para posteriormente aplicar la política de manejo más apropiada. Estas políticas puede ser parte del propio aspecto de gestión de excepciones. Como en el caso del registro de eventos, la implementación de este aspecto buscará reducir su implementación (y aplicación) a un atributo que decore el método cuyas posibles excepciones se quieren gestionar.

Un Apropiado Soporte

Al crear un mecanismo de gestión y manejo de excepciones, no deberíamos partir desde cero, ya que existe un montón de excelentes productos y frameworks que nos simplificarían esta tarea. Para este caso, emplearé los Microsoft Libraries, en particular el Exception Handling Application Block; sin embargo el enfoque de AOP y de este framework que he venido presentado permite emplear cualquier otra librería.

Implementación

Es importante que si no han leido las primeras partes de esta serie de artículos, aprovechen este momento para hacerlo, sobre todo la parte 1. A partir de este momento mis explicaciones considerarán que el conocimiento y los detalles técnicos explicados en esa parte ya son conocidos.

El primer paso es crear el atributo (Attribute) que decorará las clases/interfaces para capturar su ejecución e inyectar el código de manejo de excepciones. Nuestro atributo se llamará ExceptionHandlingAttribute y extiende de InterceptableAttribute, la clase base en el framework de AOP que sirve para definir atributos de intercepción.

    /// <summary>
    /// A custom attribute for exception handling purposes under an Aspect Oriented Programming paradigm.
    /// </summary>
    /// <remarks>
    /// It applies only to methods.
    /// </remarks>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public sealed class ExceptionHandlingAttribute : InterceptableAttribute
    {
        /// <summary>
        /// Represents the exception handling politic to apply while intercepting with this concern.
        /// </summary>
        private ExceptionHandlingPolitic politic;

        /// <summary>
        /// Initializes a new instance of the <see cref="ExceptionHandlingAttribute" /> class.
        /// </summary>
        /// <param name="politic">The exception handling politic to follow.</param>
        public ExceptionHandlingAttribute(ExceptionHandlingPolitic politic)
        {
            this.politic = politic;
            this.Processor = Activator.CreateInstance(typeof(ExceptionHandlingProcessor)) as IProcessor;
        }

        /// <summary>
        /// Gets the specified exception handling politic.
        /// </summary>
        /// <value>
        /// Returns the politic to use when handling exceptions represented as a <see cref="ExceptionHandlingPolitic"/>.
        /// </value>
        public ExceptionHandlingPolitic Politic { get { return this.politic; } }
    }

Este atributo tiene una (1) propiedad muy importantes llamada Politic la cual permite establecer la política con la que se gestionará la excepción.

En el tema de políticas de manejo de excepciones hay muchas opiniones, controversias y religiones. En lo personal, dependen mucho de las características del sistema y del entorno donde se desplegará, pero habitalmente me decanto por las siguientes tres (3) políticas:

  • Encapsular: o encapsulate en inglés, es la política más habitualmente aplicada en la gestión de excepciones. Consiste en retornar una excepción nueva más significativa al llamador, pero agregando o anexando (encapsular) la excepción original en la nueva excepción. Esta política preserva el stack trace.
  • Reemplazar: o repace en inglés, consiste en retornar una nueva excepción más significativa al llamador del método, pero si agregar o anexar la excepción original; incluso pudiéndose cambiar el mensaje de la excepción. En esta política el stack trace no se preserva.
  • Propagar: o propagate en inglés, se refiere al acto de pasar la excepción al llamador del método. Esto no quiere decir que no se gestione la excepción per se, sino que no se transformará o encapsulará esta. El stack trace no sufre modificación alguna.

Utilizo estas políticas, porque la mayoría de mis diseños de software siguen un enfoque de capas, donde las excepciones que ocurren dentro de una misma capa son propagadas, cuando se pasa de una capa inferior a una capa superior se encapsulan, y cuando se tienen que llevar a la capa de UI o presentar la excepción al usuario, entonces se reemplaza (para evitar mostrar datos e información sensible a un potencial atacante). Otros enfoques arquitectónicos como el MVC o el MVVC muy probablemente empleen políticas totalmente diferentes.

Así, la propiedad Politic del atributo ExceptionHandlingAttribute admite valores del siguiente enumerado que representa las políticas antes mencionadas:

    /// <summary>
    /// Defines the exception handling politics to use.
    /// </summary>
    public enum ExceptionHandlingPolitic
    {
        /// <summary>
        /// Represents the propagation politic. 
        /// This politic specifies that every exception will be passed to the caller without any transformation.
        /// </summary>
        Propagate,

        /// <summary>
        /// Represents the wrap politic. 
        /// This politic specifies that every exception will be wrapped inside another exception and then passed to the caller.
        /// </summary>
        Wrap,

        /// <summary>
        /// Represents the replacement politic. 
        /// This politic specifies that every exception will be replaced for another exception; then the new exception will be passed to the caller.
        /// </summary>
        /// <remarks>
        /// The original exception will be lost.
        /// </remarks>
        Replace
    }

Una vez establecido el atributo de intercepción, procedemos a crear el Processor que se encargará de manejar la inyección de código antes y después de la ejecución de los métodos interceptados. Para esto crearemos la clase ExceptionHandlingProcessor que implementará la interfaz IProcessor del framework.

Esta clase es la que dependiento del valor de la propiedad Politic y basado en el Microsoft Enterprise Library Exception Handling Application Block procederá a manejar la excepción interceptada acorde a la configuración del mencionado Application Block.

La parte interesante es que para el caso de la gestión de excepciones no se necesitará el método ProcessCallMessage, ya que las excepciones como tal sólo ocurren como un retorno de método, con lo cual toda la acción ocurrirá en el método ProcessReturnMessage.

    /// <summary>
    /// Intercep processor for the calling and returning messages of the methods decorated as interceptable by this component.
    /// </summary>
    internal class ExceptionHandlingProcessor : IProcessor
    {
        /// <summary>
        /// Constant that identifies the exception handling policy of propagation.
        /// </summary>
        private const string PROPAGATE = @"PROPAGATE";

        /// <summary>
        /// Constant that identifies the exception handling policy of replacement.
        /// </summary>
        private const string REPLACE = @"REPLACE";

        /// <summary>
        /// Constant that identifies the exception handling policy of wrap.
        /// </summary>
        private const string WRAP = @"WRAP";

        /// <summary>
        /// Processes the call message, which happens before the execution of the body of the interceptable or processable method.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Since the exception handling only occurs after the execution of the body of the interceptable or processable method, this
        /// method does nothing.
        /// </para>
        /// <para>
        /// This is because the exception type is contained in the <c>Method Return Message</c>.
        /// </para>
        /// </remarks>
        /// <param name="callMessage">The <c>Method Call Message</c> to process.</param>
        /// <param name="processable">The processable decorated object to process.</param>
        public void ProcessCallMessage(IMethodCallMessage callMessage, InterceptableAttribute processable)
        {
            // Do Nothing.
        }

        /// <summary>
        /// Processes the return message, which happens after the execution of the body of the interceptable or processable method.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Since the <c>Method Return Message</c> contains the exception type, this method processes such message to retrieve the exception
        /// information.
        /// </para>
        /// <para>
        /// This method leverages the Microsoft Enterprise Library Exception Handling Application Block.
        /// </para>
        /// </remarks>
        /// <param name="returnMessage">The <c>Method Return Message</c> to process.</param>
        /// <param name="processable">The processable or interceptable object.</param>
        public void ProcessReturnMessage(IMethodReturnMessage returnMessage, InterceptableAttribute processable)
        {
            if (returnMessage != null && returnMessage.Exception != null)
            {
                string policyName = null;

                switch (((ExceptionHandlingAttribute)processable).Politic)
                {
                    case ExceptionHandlingPolitic.Propagate:
                        policyName = PROPAGATE;
                        break;

                    case ExceptionHandlingPolitic.Replace:
                        policyName = REPLACE;
                        break;

                    case ExceptionHandlingPolitic.Wrap:
                        policyName = WRAP;
                        break;

                    default:
                        throw new InvalidOperationException(@"Invalid or not defined exception handling politic.");
                }

                // Call to the static main entrance of the Microsoft Enterprise Library Exception Application Block.
                if (ExceptionPolicy.HandleException(returnMessage.Exception, policyName))
                {
                    throw returnMessage.Exception;
                }
            }
        }
    }

Como puede apreciarse en el método ProcessReturnMessage lo primero es saber si el método interceptado a retornado una excepción, lo cual se puede determinar fácilmente si la priedad Exception de la interfaz IMethodReturnMessage (a la cual pertenece el parámetro returnMessage) no es nula (null).

De haber una excepción, basta con recuperar el valor de la propiedad Politic del atributo para saber que política aplicar a la excepción interceptada. La configuración de estas políticas están delegadas al archivo de configuración del Exception Handling Application Block, y su aplicación se realiza en la línea 79.

Como en artículos anteriores, aquí tienen el código de este aspecto para estudiarlo. Espero que les sea de utilidad.

Cargar Dinámicamente Configuraciones de Entidades en Entity Framework y Code-First

En estos días he estado muy activo empleando la nueva versión del Entity Framework de Microsoft.

Una de las cosas que hace poderoso a este framework y que en particular a mi me encanta es la posibilidad de definir y configurar todo el repositorio de datos (la base de datos per se) completamente desde código fuente gestionado, un enfoque que se conoce como Code-First.

A través de Code-First es posible definir las clases POCO y también configuradores (clases que bien heradan de EntityTypeConfiguration o de ComplexTypeConfiguration) y que nos permiten definir las restricciones sobre el modelo (como claves primarias y no nulidades) o los nombres de las tablas y sus columnas.

La parte interesante es que en modelos grandes y complejos un desarrollo termina llenándose de muchos configuradores que deben ser agregados al modelo en el momento de su creación (lo cual ocurre durante el evento OnModelCreating de la clase DbContext) y que termina incrementando dos factores muy poco positivos de un código: la complejidad ciclomática y el acoplamiento de clases, ambos elementos que son reportados por el Visual Studio a través del Code Analyzer como una violación a la regla CA1506.

Para evitar esto, lo ideal seria encontrar un mecanismo para cargar los configuradores de forma dinámica; pero el problema es que el método Add para el ModelBuilder que se emplea para cargarlos no admite como parámetro una clase base o genérica que pudiera ayudarnos a través de herencia, sino que explícitamente sólo acepta un EntityTypeConfiguration o un ComplexTypeConfiguration.

Por suerte, la plataforma .NET y el lenguaje C# cuentan con una palabra que logra salvar la patria: dynamic; que se encargará de indicar al compilador que no haga validaciones del tipo de dato/objeto que se está pasando como parámetro al método y que más bien este tipo de verificaciones se practiquen en tiempo de ejecución, es decir, que el compilador confíe en que nosotros como desarrolladores sabemos que estamos haciendo.

Así, el primer paso sería utilizar Reflection sobre nuestro ensamblado (que como ya está cargado en memoria durante la ejecución del evento OnModelCreating no impactará [negativamente] en el desempeño de nuestro aplicativo)

/// <summary>
/// Method calleded when the model for a derived context has been initialized, but before the model has 
/// been locked down and used to initialize the context.
/// </summary>
/// <param name="modelBuilder">The builder that defines the model for the context being created.</param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    if (modelBuilder != null)
    {
        IEnumerable<Type> entityTypeConfigurationTypes = Assembly.GetExecutingAssembly().GetTypes().Where(type => !type.IsAbstract 
        && (type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration) || type.BaseType.GetGenericTypeDefinition() == typeof(ComplexTypeConfiguration)));

        foreach (Type type in entityTypeConfigurationTypes)
        {
            // The 'Add' method of the DbModelBuilder accepts both "ComplexTypeConfiguration<TComplexType>" or "EntityTypeConfiguration<TEntityType>".
            // At first glance, the type of configurator is not know during compile time.
            // The dynamic type enables the operations in which it occurs to bypass compile-time type checking, in other words,
            // types are not resolved or checked by the compiler; instead, these operations are resolved at run time (when the explicit configuration
            // type is known).
            // Reference: http://msdn.microsoft.com/en-us/library/dd264741.aspx
            dynamic entityTypeConfigurationInstance = Activator.CreateInstance(type);
            modelBuilder.Configurations.Add(entityTypeConfigurationInstance);
        }
    }
}

De esta manera incluso podemos modificar el acceso a las clases de configuración como internal; sin embargo será entonces necesario ignorar el warning CA1812 del Code Analyzer (la cual podemos suprimir de forma segura ya que sabemos que sí son consumidas por Reflection).

Actualización 21/01/2013: En la nueva versión del Entity Framework, la 6.0, encontraremos en la clase ConfigurationRegistrar un nuevo método llamado AddFromAssembly que nos permitirá hacer exactamente lo que se describe en este artículo. La clase ConfigurationRegistrar habitualmente la accedemos desde el método/evento OnModelCreating a través de la propiedad Configurations.

Referencias:

Un Framework para Programación Orientada a Aspectos (AOP) en C#    Parte 2: Un aspecto para registro de eventos

Esta publicación es la tercera parte (aunque diga parte dos) de una serie de cuatro publicaciones que estaré realizando sobre programación orientada a aspectos en .NET empleando el lenguaje de programación C#.

El que es quizás sea el ejemplo más sencillo de un aspecto es el registro de eventos de un sistema o aplicación. Así mismo, resulta ser el más significativo también.

El típico infierno al que se enfrenta un arquitecto o líder técnico a la hora de establecer la política de registro de eventos en su diseño es la gobernabilidad de dicho diseño, y la monitorización de su cumplimiento por parte del equipo de desarrollo, cosa que se torna excesivamente complicada por la creatividad de algunos miembros del equipo que rompe la homogeneidad de los mensajes que se ha establecido como parte del modelo de eventos.

Otro típico incidente es que los desarrolladores, muchas veces culpando a las restricciones de tiempo del proyecto o a la urgencia de la solicitud en la implementación de un determinado requerimiento, omiten agregar las líneas de código que generan el registro de eventos, lo cual a veces puede resultar incluso difícil de detectar en revisiones de código (a través de prácticas como los peer reviews.

A través del enfoque de AOP, es posible esconder el mensaje del evento y reducir su implementación a colocar un atributo que decore el método que se desea loggear, lo cual permitirá establacer el formato del mensaje e impedir que los miembros del equipo lo modifiquen, reforzando la arquitectura y la gobernabilidad del proyecto, a la vez que se simplifica el proceso de desarrollo y de verificación del código.

Un Apropiado Soporte

Al crear un sistema de registro de eventos, no deberíamos partir desde cero, ya que existe un montón de excelentes productos y frameworks que nos simplificarían esta tarea. Para este caso, emplearé los Microsoft Libraries, en particular el Logging Application Block; sin embargo el enfoque de AOP y de este framework que he venido presentado permite emplear cualquier otra librería como log4net o NLog.

Implementación

Es importante que si no han leido las primeras partes de esta serie de artículos, aprovechen este momento para hacerlo, sobre todo la parte 1. A partir de este momento mis explicaciones considerarán que el conocimiento y los detalles técnicos explicados en esa parte ya son conocidos.

El primer paso es crear el atributo (Attribute) que decorará las clases/interfaces para capturar su ejecución e inyectar el código de registro de eventos. Nuestro atributo se llamará LogAttribute y extiende de InterceptableAttribute, la clase base en el framework de AOP que sirve para definir atributos de intercepción.

    /// <summary>
    /// A custom attribute for logging purposes under an Aspect Oriented Programming paradigm.
    /// </summary>
    /// <remarks>
    /// It applies only to methods.
    /// </remarks>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public sealed class LogAttribute : InterceptableAttribute
    {
        /// <summary>
        /// A stopwatch to use then the verbority level is set to its highest value, in order to
        /// log the amount of time consumed by methods calls.
        /// </summary>
        private Stopwatch timer;

        /// <summary>
        /// The verbosity level to log.
        /// </summary>
        private VerbosityLevel verbosity;

        /// <summary>
        /// Initializes a new instance of the <see cref="LogAttribute" /> class.
        /// </summary>
        /// <param name="verbosity">The verbosity level to log.</param>        
        public LogAttribute(VerbosityLevel verbosity)
        {
            this.verbosity = verbosity;
            this.Processor = Activator.CreateInstance(typeof(LogProcessor)) as IProcessor;

            if (verbosity.Equals(VerbosityLevel.Full))
            {
                this.timer = new Stopwatch();
                this.timer.Reset();
            }
        }

        /// <summary>
        /// Gets the verbosity level to log.
        /// </summary>
        /// <value>The verbosity level of the log trace as defined in <see cref="VerbosityLevel"/>.</value>
        public VerbosityLevel Verbosity { get { return this.verbosity; } }

        /// <summary>
        /// Gets the this log timer.
        /// </summary>
        /// <remarks>
        /// If the <c>Verbosity</c> is different than <see cref="VerbosityLevel.Full"/> then this property will return <c>null</c>.
        /// </remarks>
        /// <value>
        /// An instance of a <see cref="Stopwatch"/> to retrieve execution time when logging with <see cref="VerbosityLevel.Full"/>.
        /// </value>
        /// <see cref="Stopwatch"/>
        /// <see cref="AOPLoggingApplicationBlock.VerbosityLevel"/>
        public Stopwatch Timer { get { return this.timer; } }
    }

Este atributo tiene dos (2) propiedades muy importantes. La primera, llamada VerbosityLevel permite establecer que tanta información se generará/almacenará cuando se registre eventos con cada intercepción. En principio para este ejemplo he definido cuatro (4) niveles de verbosidad:

  1. None: no se registrarán eventos.
  2. Light: una mínima, pero significativa cantidad de información será generada.
  3. Medium: una cantidad moderada de significante información será generada.
  4. Full: mucha información será generada, no necesariamente todoa ella significativa, pero sí útil.

La segunda propiedad se llama Timer no es más que un Stopwatch y que para el caso de VerbosityLevel.Full nos medirá cuanto ha tardado en ejecutarse un método interceptado (con lo cual podríamos identificar cuellos de botella en tiempos de producción por ejemplo).

Una vez establecido el atributo de intercepción, procedemos a crear el Processor que se encargará de manejar la inyección de código antes y después de la ejecución de los métodos interceptados. Para esto crearemos la clase LogProcessor que implementará la interfaz IProcessor del framework.

Esta clase es la que dependiento del VerbosityLevel y basado en el Microsoft Enterprise Library Logging Application Block procederá a escribir registros acorde a la configuración del mencionado Application Block. Los métodos ProcessCallMessage y ProcessReturnMessage se encargarán de crear el mensaje del evento de una manera estandard y pre-establecida (evitando que los desarrolladores decidan el mensaje y rompan con la gobernabilidad).

Lamentablemente la implementación de la clase LogProcessor es un tanto extensa como para ponerla en esta publicación, sin embargo está disponible para descargarse (y estudiarse) justo aquí.

Instalando la plataforma .NET 4.0.3

Como seguro saben, hace cosa de cuatro meses salió una revisión de la versión 4 de la plataforma .NET.

Con versión 4.0.3, esta revisión incluye en principio varias correcciones y nuevos miembros y tipos muy solicitados por la comunidad de desarrolladores entre las cuales destacan las siguientes:

Característica / Funcionalidad 4.0.1 4.0.2 4.0.3
Soporte a la ejecución de workflows con confianza parcial.    
Actividades de mensajería de Windows Workflow Foundation (WWF) habilitada para múltiples hospedajes (hosts).    
Nuevo enumerado GCLatencyMode que configuran el comportamiento del garbage collection.    
Soporte para identificadores corelacionados en conexiones de SqlClient.    
Soporte para las nuevas clases y librerías portables (portable classes de la plataforma .NET 4.    
Soporte para AlwaysOn en el SqlClient  
Soporte para la ejecución con el SqlClient para base de datos locales de SQL Server Express.  
Soporte para diseñar y ejecutar StateMachine.
Soporte para SqlWorkflowInstanceStore sobre SQL Azure.
Compensación y balanceo para hosts personalizados.

Para poder disfrutar de esta nueva versión existen tres (3) instalables:

  1. Para desarrollar aplicaciones en el Visual Studio 2010, instalar el paquete de diseño disponible aquí.
  2. Para ejecutar aplicaciones creadas con esta revisión, instalar el paquete de ejecución (run-time) disponible aquí.
  3. Si ya disfrutan y emplean el nuevo Visual Studio 2012, entonces se necesita el paquete multi-target disponible aquí.

Referencias:

Probando el Visual Studio 2012 Release Candidate

Visual Studio 2012

En estos días salió disponible al público adicto al desarrollo en la plataforma .NET la nueva version del Visual Studio 2012, la cual se puede descargar desde aquí. Esta versión ya no está marcada como Beta, sino como Release Candidate; lo cual significa que los próximos releases no tendrán tantos cambios notables como veremos entre esta versión y la anterior Visual Studio 2011 (o quizás me equivoque).

Lo que me gusto…

Cuando estaba en el colegio, para graduarme tenía que presentar una tesis de grado de tópico científico sobre un tema que me apacionara. Ya desde jóven me atraía la computación/informática, así que con mi amigo Juan Carlos Araujo (@JC_Araujo_S) nos dispusimos a crear una aplicación en Visual Basic 6.0 (que estaba recién salido del horno) para crear un sistema que facilitara a los profesores realizar sus actividades del día a día. Una de las características del sistema que diseñamos era emplear un código de colores que a través de su caracter cognitivo permitirera identificar rápidamente en que estado se encontraba el usuario dentro de la aplicación.

Con Visual Studio 2012 vemos justamente ese enfoque de emplear colores para que se pueda identificar rápidamente (casi subconcientemente) en que estado nos encontramos.

En el mismo tono, me encantó que la gente de Microsoft escuchara a la comunidad de desarrolladores, ya que la versión Beta de 2011 era demasiado opaca, plana, de poco contraste y demasiado monocroma. Con la versión 2012 vemos que la iconografía tiene mejor contraste y mayor colorido, sin romper en mi opinión con la filosofía de METRO UI.

El debugger está muy mejorado, sobre todo para trabajar con JavaScript; cosa que asumo es un esfuerzo por parte de Microsoft de ofrecer un IDE para el desarrollo de METRO Apps que son muy basadas en HTML5 y JavaScript. La ventana de “Callstack” para depurar aplicaciones web está muy mejorada, lo cual se agradece.

Otra cosa que me gusta es que la ventana de “Solution Explorer” ahora ofrece más información sobre las características de los archivos y clases, como sus métodos, propiedades y atributos. Sin embargo creo que necesita trabajarse un poco más antes de la versión final.

Lo que no me gusto…

Una de las cosas que me han chocado es la UX. Como desarrollador, trato de maximizar lo más posible el espacio disponible para el código fuente, lo que significa que la mayoría de las ventanas como el “Toolbox”, “Output”, el “Team Explorer” o el “Solution Explorer” las tengo escondidas (que en VS2010 por ejemplo se ven como pestañas) pero disponibles siempre que pase el mouse por encima. Con esta versión, estas ventanas no se ven como pestañas sino como botones planos, y siempre que quiera ver alguna tengo que hacer click sobre estos; lo que significa que tengo que hacer “más” clicks para ser “productivo”.

Estéticamente me sigue pareciendo muy plano. Está mejor que la versión 2011, sin embargo sigue siendo demasiado homogeneo y siento que eso hace que sea confuso o relentizante encontrar la opción funcional que se busca.

Haz click para ver la imagen más grande.

Otra cosa que me chocó es que los menús están en mayúsculas. Aunque es un enfoque que busca acercar más la UI a la filosofía de METRO UI, creo que no es la mejor desición.

Finalizando…

Esta es una versión de Visual Studio que no podemos perdernos. Una versión histórica que nos mostrará que tan lejos puede llegar un IDE dentro de un nuevo mundo Post-PC, para desarrollar aplicaciones móviles y de tablets bajo los nuevos paradigmas táctiles.

Sin embargo creo que la UI y la UX siguen necesitando una mano más de pintura, un poco más de arte y menos de futurism y color plano.

Mi mejor recomendación, es instalarlo en un Windows 8, que casualmente también tiene una nueva versión de pruebas disponible para descargar aquí, y desde el cual he escrito esta publicación.

Finalmente, la gente de Microsoft nos ha dejado una entrada en sus blogs con un completo listado y descripción de los cambios que encontraremos en esta nueva versión.

Un Framework para Programación Orientada a Aspectos (AOP) en C#    Parte 1: Implementación

Esta publicación es la segunda parte (aunque diga parte uno) de una serie de cuatro publicaciones que estaré realizando sobre programación orientada a aspectos en .NET empleando el lenguaje de programación C#.

Hace un tiempo, trabajando con Diego Herrera(@vermicida) decidimos indagar un poco dentro del mundo del AOP y crear un framework que de forma universal permitiera a cualquier desarrollo en .NET crear pequeñas librerías que sirvieran para satisfacer una funcionalidad contextual como por ejemplo el registro de eventos (logging), la gestión de excepciones (exception handling), mecanismos de seguridad (como autenticación continua con cada invocación a métodos) o monitoreo de desempeño del sistema (performance monitoring).

Nuestra principal necesidad al crear este framework era que los desarrolladores tuviesen las herramientas necesarias para aplicar las diferentes funcionalidades (aspects) transversales (cross cutting) de manera eficiente, sin resultar una carga que relentizara el tiempo de implementación pero que a la ves, desde un punto de gobernabilidad del proceso de desarrollo, no fueran capaces de omitir la colocación de dicha funcionalidad, es decir, evitar por ejemplo el típico caso de “como son muchas cosas se me pasó escribir el código que loggea eventos”, ó “Es que no sabía que colocar”.

El mecanismo elegido es el uso de atributos de no más de dos (2) parámetros como máximo y de aplicación sobre métodos, de forma tal que los desarrolladores sólo tengan que colocar el atributo correspondiente al aspecto que se desea aplicar en los métodos en los que sea requerido, siendo la configuración del aspecto establecida por parámetros. Estos parámetros tendrán un valor por defecto lo suficientemente significativo para el aspecto para cubrir aquellos casos en los que los desarrolladores los omitan. Un ejemplo de un programa que emplearía este framework sería el siguiente:

...
    public class TestObject : InterceptableObject
    {
        [Log(VerbosityLevel.None)]
        public void MethodA() { }

        [Log(VerbosityLevel.Light)]
        public void MethodB(string str) { }

        [Log(VerbosityLevel.Medium)]
        public string MethodC(string name)
        {
            return String.Format(@"Hola {0}", name);
        }
        
        ...
    }
...

En el código anterior, el atributo Log es un aspecto desarrollado empleando este framework.

Al final del día lo que buscábamos era un mecanismo eficiente, simple, sencillo, y que ofreciera al equipo de desarrollo la posibilidad de agregar las funcionalidades transversales, evitando desvíos en la gobernabilidad de las desiciones y conceptos de diseño de la arquitectura (como el formato de los mensajes de excepción por ejemplo).

Implementación

La siguiente imagen corresponde al diagrama de clases de este framework y cuyos elementos iremos describiendo y detallando a lo largo de esta publicación:

Diagrama de Clases

Haz click para ver la imagen más grande.

Recordando de la parte cero de esta serie de publicaciones, en .NET para que una clase sea interceptable debe cumplir con dos (2) condiciones:

  1. Debe extender de la clase ContextBoundObject
  2. Debe estar decorada con un atributo que extienda de ContextAttribute

Un objeto que extiende de ContextBoundObject es como el Buda, adquiere conciencia de sí mismo, de su relación con el universo (el CLR) y con los demás. Esto es muy útil porque podemos de alguna manera saber cuando se va a ejecutar un método dentro del objeto y cuando dicho método a finalizado su ejecución, que es justamente lo que se busca hacer en AOP para interceptar e intervenir la ejecución de los métodos e inyectar la funcionalidad del aspecto que se quiere introducir.

Sin embargo, un ContextBoundObject por sí sólo es incapaz de procesar la inyección en sí de código para los aspectos, sólo es capaz de decir cuando un método es invocado o retorna de su invocación; de ahí que se tenga que decorar con un ContextAttribute, que agregará a la meta-data de la clase toda la información y mecanismos necesarios para interactuar con su contexto, lo que en el caso de este framework permitirá redirigir la ejecución a las porciones de funcionalidad de los aspectos a inyectar.

Pero en una arquitectura de N-capas con todo un dominio de clases susceptibles de ser interceptadas, podemos encontrarnos con que es realmente incómodo, poco escalable y flexible el tener que hacer que (todas) las clases de la arquitectura que requieran intercepción extiendan de ContextBoundObject, y que estén decoradas con el atributo ContextAttribute. Puede pasar que a algún desarrollador se le olvide extender de ContextBoundObject o aplicar el atributo ContextAttribute, y una de las intenciones de este framework es evitar que estas situaciones pasen.

Lo ideal es que las clases de la arquitectura extiendan de una única clase padre, que sea la que esté decorada con el atributo ContextAttribute y que extienda de ContextBoundObject, y que llamaremos InterceptableObject:

using System;
using System.Security.Permissions;

namespace AOPFramework
{
    /// <summary>
    /// Represents a context-bound object whose execution must be intercepted.
    /// </summary>
    [InterceptContextAttribute]
    public abstract class InterceptableObject : ContextBoundObject
    {
        /// <summary>
        /// Class constructor.
        /// </summary>
        protected InterceptableObject() : base() { }

        /// <summary>
        /// Obtains a lifetime service object to control the lifetime policy for this instance.
        /// </summary>
        /// <returns>Returns <c>null</c> since a lifetime service is not defined for interceptable objects.</returns>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public override object InitializeLifetimeService()
        {
            return null;
        }
    }
}

Hemos definido esta clase como abstracta para impedir que pueda ser instanciada, obligando a todas las clases de la arquitectura consumidora de este framework a tener que extender de ella y de esta forma generar una familia de clases que podría ser identificadas de forma unívoca como “interceptables”.

Igualmente puede apreciarse que la clase InterceptableObject está decorada con un attributo de tipo InterceptContextAttribute, que no es más que una especificación de la clase ContextAttribute:

using System;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Contexts;
using System.Security.Permissions;

namespace AOPFramework
{
    /// <summary>
    /// Defines an attribute that will decorate those classes that can be intercepted in order to process its behaviour, represented by
    /// the invokation of its methods.
    /// </summary>
    /// <remarks>
    /// Class extends from <c>ContextAttribute</c>, which provides the default implementations of the <c>IContextAttribute</c> and <c>IContextProperty</c> interfaces.
    /// </remarks>
    [AttributeUsage(AttributeTargets.Class, Inherited = true)]
    internal sealed class InterceptContextAttribute : ContextAttribute
    {
        /// <summary>
        /// Class constructor.
        /// </summary>
        public InterceptContextAttribute() : base(Guid.NewGuid().ToString()) { }

        /// <summary>
        /// Called when the context is frozen.
        /// </summary>
        /// <param name="newContext">The context to freeze.</param>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public override void Freeze(Context newContext) { }

        /// <summary>
        /// Adds the current context property to the given message.
        /// </summary>
        /// <param name="ctorMsg">The <c>IConstructionCallMessage</c> to which to add the context property.</param>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
        {
            if (ctorMsg != null)
            {
                ctorMsg.ContextProperties.Add(new InterceptProperty(this.Name));
            }
        }

        /// <summary>
        /// Returns a Boolean value indicating whether the context parameter meets the context attribute's requirements.
        /// </summary>
        /// <param name="ctx">The context in which to check.</param>
        /// <param name="ctorMsg">The <c>IConstructionCallMessage</c> to which to add the context property.</param>
        /// <returns>
        /// This method will return <c>true</c> if the passed in context is okay; otherwise, it will return <c>false</c>.
        /// </returns>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public override bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)
        {
            return (ctx != null) && (ctx.GetProperty(this.Name) as InterceptProperty) != null;
        }

        /// <summary>
        /// Returns a Boolean value indicating whether the context property is compatible with the new context.
        /// </summary>
        /// <param name="newCtx">The new context in which the property has been created.</param>
        /// <returns>
        /// This method will return <c>true</c> if the context property is okay with the new context; otherwise, it will return <c>false</c>.
        /// </returns>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public override bool IsNewContextOK(Context newCtx)
        {
            return (newCtx != null) && (newCtx.GetProperty(this.Name) as InterceptProperty) != null;
        }
    }
}

Como la idea es crear un framework, y parte de ello significa esconder la mayor cantidad de complejidad a futuros consumidores, la clase InterceptContextAttribute se implementa como internal para que no pueda ser referenciada fuera del assembly del framework y sea empleada únicamente por la clase InterceptableObject.

Nuevamente recordando de la parte cero de esta serie de publicaciones, en .NET cuando se trabaja con clases de contexto, existe el concepto de sumidero (o sink): clases responsables de gestionar los mensajes que genera la clase, determinando la apropiada y correspondiente inyección de aspectos. La clase InterceptProperty, que es referenciada en la anterior InterceptContextAttribute, es responsable de comunicar al contexto el tipo (Type) correspondiente al sumidero que gestionará los mensajes que se generen.

using System;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;

namespace AOPFramework
{
    /// <summary>
    /// This class implements the necessary methods to represent a property that can be intercepted and provide information as well as
    /// data to the context sink.
    /// </summary>
    internal class InterceptProperty : IContextProperty, IContributeClientContextSink, IContributeServerContextSink
    {
        /// <summary>
        /// Class constructor.
        /// </summary>
        public InterceptProperty(string name) : base() { this.Name = name; }

        /// <summary>
        /// Gets the name of the property under which it will be added to the context.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Called when the context is frozen.
        /// </summary>
        /// <param name="newContext">The context to freeze.</param>
        public void Freeze(Context newContext) { }

        /// <summary>
        /// Returns a Boolean value indicating whether the context property is compatible with the new context.
        /// </summary>
        /// <param name="newCtx">The new context in which the ContextProperty has been created.</param>
        /// <returns>
        /// <c>True</c> if the context property can coexist with the other context properties in the given context; otherwise, <c>false</c>.
        /// </returns>
        public bool IsNewContextOK(Context newCtx)
        {
            return (newCtx != null) && (newCtx.GetProperty(this.Name) as InterceptProperty) != null;
        }

        /// <summary>
        /// Chains the message sink of the provided server object in front of the given sink chain.
        /// </summary>
        /// <param name="nextSink">The chain of sinks composed so far.</param>
        /// <returns>The composite sink chain.</returns>
        public IMessageSink GetClientContextSink(IMessageSink nextSink)
        {
            return new InterceptSink(nextSink);
        }

        /// <summary>
        /// Chains the message sink of the provided server object in front of the given sink chain.
        /// </summary>
        /// <param name="nextSink">The chain of sinks composed so far.</param>
        /// <returns>The composite sink chain.</returns>
        public IMessageSink GetServerContextSink(IMessageSink nextSink)
        {
            return new InterceptSink(nextSink);
        }
    }
}

Nótese que la clase implementa tres (3) interfaces:

Ahora viene la parte complicada en el diseño del framework. Independientemente de si el mensaje entra o sale del contexto, la clase InterceptProperty retornará una instancia de la clase InterceptSink, que será el único sumidero que interese para procesar los mensajes. Es decir, que todos los mensajes serán procesados por el mismo sumidero independientemente del momento de su intercepción.

Estos mensajes son producidos por la invocación o retorno de los métodos de las clases que extiendan de InterceptableObject, los cuales se espera estén decorados con atributos que extienden de la clase InterceptableAttribute, y que permite emplear este framework para crear atributos custom (tal y como se mostró en la primera porción de código al inicio de esta publicación) que servirán para indicar en el código fuente la inyección de un aspecto.

using System;

namespace AOPFramework
{
    /// <summary>
    /// Defines or decorates a class as interceptable. An interceptable class can be processed by a class that implements the <c>IProcessor</c> interface.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public abstract class InterceptableAttribute : Attribute
    {
        /// <summary>
        /// Sets or gets the processor for this class.
        /// </summary>
        public IProcessor Processor { get; set; }

        /// <summary>
        /// Sets or gets this attribute processing priority.
        /// </summary>
        /// <remarks>
        /// The bigger the value of this property, the bigger the importance or priority of processing
        /// this attribute.
        /// </remarks>
        public int Priority { get; set; }
    }
}

El atributo InterceptableAttribute permite agregar dos propiedades a la meta-data de los métodos que se decoren con él: una instancia de la interface IProcess y un valor entero que define la prioridad de atención al aspecto que se modele con este atributo.

Un Tema de Prioridad

El argumento detrás de una propiedad para definir una prioridad es que en .NET al recuperar los atributos (por reflection por ejemplo), estos aparecen en el órden inverso a como fueron colocados en el código. Esto significa que los programadores estarían restringidos a colocar los atributos que definen aspectos en el órden preciso que se necesita para su inyección, lo cual primero no es flexible y segundo puede llevar a escenarios que rompan con la gobernabilidad que haya decidido el Arquitecto de Software o el Líder Técnico para la inyección de los aspectos.

Propiedad Prioridad

Definiendo una prioridad para un aspecto como parte de sus propiedades y ordenando luego los aspectos que se deben aplicar a un método acorde a dicha prioridad se otorga a los programadores la libertad de agregar los atributos que los definen en el orden que deseen. Más aún, si durante la vida de un proyecto se crean o descartan aspectos, sólo bastará con agregar o remover el atributo correspondiente sin tomar en cuenta si esta de primero o último en la pila de atributos (ganando flexibilidad y escalabilidad en el diseño e implementación).

Un Tema de Procesamiento

La otra propiedad que agrega a la meta-data de los métodos decorados con el atributo InterceptableAttribute es la posibilidad de obtener una instancia de la inteface IProcess que definirá el contrato que debe seguir los aspectos que se programen con este framework:

using System;
using System.Runtime.Remoting.Messaging;

namespace AOPFramework
{
    /// <summary>
    /// Defines the interface for those objects that will process the calling and return messages for a class that extends 
    /// from <c>InterceptableObject</c>.
    /// </summary>
    /// <seealso cref="AOPFramework.InterceptableObject"/>
    public interface IProcessor
    {
        /// <summary>
        /// Process a <c>Method Call Message</c>.
        /// </summary>
        /// <remarks>
        /// This method will be called before the execution of the body of an interceptable or processable object.
        /// </remarks>
        /// <param name="callMessage">The <c>Method Call Message</c> to process.</param>
        /// <param name="processable">The processable decorated object to process.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage(@"Microsoft.Naming", @"CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = @"processable", Justification = @"According to Merriam-Webster dictionary, the word 'Processable' exists and is correctly spelled (http://www.merriam-webster.com/dictionary/processable).")]
        void ProcessCallMessage(IMethodCallMessage callMessage, InterceptableAttribute processable);

        /// <summary>
        /// Process a <c>Method Return Message</c>.
        /// </summary>
        /// <remarks>
        /// This method will be called after the execution of the body of an interceptable or processable object. The <c>returnMessage</c> parameter will
        /// contain the result of the execution, including any possible exception type.
        /// </remarks>
        /// <param name="returnMessage">The <c>Method Return Message</c> to process.</param>
        /// <param name="processable">The processable decorated object to process.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage(@"Microsoft.Naming", @"CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = @"processable", Justification = @"According to Merriam-Webster dictionary, the word 'Processable' exists and is correctly spelled (http://www.merriam-webster.com/dictionary/processable).")]
        void ProcessReturnMessage(IMethodReturnMessage returnMessage, InterceptableAttribute processable);
    }
}

Esta interface define dos métodos:

  • ProcessCallMessage: Establece el método que debe atender el pre-procesamiento o acciones a realizar antes de ejecutar el método interceptado. Básicamente es el que sería invocado en el momento en que un ContextAttribute procede a ejecutar (llamar) a uno de sus métodos, inyectando aspectos cuya ejecución se realizará previa a la ejecución de dicho método.
  • ProcessReturnMessage: Establece el método que debe atender el post-procesamiento o acciones a realizar después de haber ejecutado el método que está siendo interceptado; es decir, inyecta aspectos cuya ejecución se realizará posterior a la ejecución de dicho método.

Retomando el Sumidero

Como se explicó previamente, la clase InterceptProperty retornará una instancia de la clase InterceptSink, que en este framework es el único sumidero necesario para atender los mensajes entrantes y salientes.

using System;
using System.Runtime.Remoting.Messaging;

namespace AOPFramework
{
    /// <summary>
    /// Represents  an intercept sink.
    /// </summary>
    internal class InterceptSink : IMessageSink
    {
        /// <summary>
        /// Stores the next message sink in the sink chain.
        /// </summary>
        private IMessageSink nextSink;

        /// <summary>
        /// A generic instance of an object to lock on, rather than locking on the type itself, to avoid deadlocks.
        /// </summary>
        /// <remarks>
        /// The initialization of this variable uses the <i>static initialization</i> approach, where the instance is created 
        /// the first time any member of the class is referenced. The common language runtime (CLR) takes care of the variable 
        /// initialization.
        /// </remarks>
        private static object syncRoot = new Object();

        /// <summary>
        /// Class constructor.
        /// </summary>
        /// <param name="nextSink">The next message sink in the sink chain.</param>
        public InterceptSink(IMessageSink nextSink)
        {
            lock (syncRoot)
            {
                this.nextSink = nextSink;
            }
        }

        /// <summary>
        /// Gets the next message sink in the sink chain.
        /// </summary>
        public IMessageSink NextSink
        {
            get { return this.nextSink; }
        }

        /// <summary>
        /// Asynchronously processes the given message.
        /// </summary>
        /// <param name="msg">The message to process.</param>
        /// <param name="replySink">The reply sink for the reply message.</param>
        /// <returns>
        /// Returns an <c>IMessageCtrl</c> interface that provides a way to control asynchronous messages after they have been dispatched.
        /// </returns>
        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            return nextSink.AsyncProcessMessage(msg, replySink);
        }

        /// <summary>
        /// Synchronously processes the given message.
        /// </summary>
        /// <param name="msg">The message to process.</param>
        /// <returns>A reply message in response to the request.</returns>
        public IMessage SyncProcessMessage(IMessage msg)
        {
            IMethodCallMessage callMessage = (msg as IMethodCallMessage);

            if (callMessage != null)
            {
                InterceptableAttribute[] processables = (InterceptableAttribute[])(msg as IMethodMessage).MethodBase.GetCustomAttributes(typeof(InterceptableAttribute), true);
                
                // Sort 'aspects' (attributes) based on the value of the 'priority' property.
                Array.Sort<InterceptableAttribute>(processables, new Comparison<InterceptableAttribute>(delegate(InterceptableAttribute a, InterceptableAttribute b) { return a.Priority.CompareTo(b.Priority); }));

                // Do pre-processing: process the method call before its body execution. The calling parameters are going to be available.
                for (int i = 0; i < processables.Length; i++)
                {
                    processables[i].Processor.ProcessCallMessage(callMessage, processables[i]);
                }

                // Do post-processing: process the method call after its body execution. The return values (or exception) are going to be available.
                IMethodReturnMessage returnMessage = (nextSink.SyncProcessMessage(callMessage) as IMethodReturnMessage);

                if (returnMessage != null)
                {
                    for (int i = 0; i < processables.Length; i++)
                    {
                        processables[i].Processor.ProcessReturnMessage(returnMessage, processables[i]);
                    }

                    return returnMessage;
                }
            }

            return msg;
        }
    }
}

El método SyncProcessMessage es el principal responsable en esta clase, y se encarga de ejecutar las siguientes acciones con cada instancia de IMessage que procesa:

  1. Se verifica que el mensaje es un mensaje de llamada (call) a un método. Es decir, que el mensaje es una instancia de la interface IMethodCallMessage.
  2. Se recuperan todos los atributos del mensaje que sean del tipo InterceptableAttribute.
  3. Se ordenan los atributos acorde a su propiedad de prioridad.
  4. Se obtiene la instancia de la clase procesadora del aspecto (de tipo IProcessor) y se invoca al método ProcessCallMessage para inyectar cualquier funcionalidad que defina el aspecto y que deba ejecutarse previo a la ejecución del cuerpo del método que está siendo interceptado.
  5. Se procede a continuar con la cadena (chain) conceptual de funcionalidades; verificando que los mensajes que se reciben sean de salida (return) de un método. Es decir, que el mensaje es una instancia de la interface IMethodReturnMessage.
  6. Nuevamente se obtiene la instancia de la clase procesadora del aspecto, y se invoca al método ProcessReturnMessage para inyectar cualquier funcionalidad que defina el aspecto y que deba ejecutarse posteriormente a la ejecución del cuerpo del método que está siendo interceptado.
  7. Se retorna el mensaje para continuar con la cadena conceptual.

Finalizando

La siguiente imagen corresponde al diagrama de dependencias de clases de este framework y que muestra la relación de los elementos descritos y detallados en esta publicación:

Haz click para ver la imagen más grande.

Para emplear este framework y crear aspectos se deben seguir los siguientes pasos:

  1. Crear un atributo para definir el aspecto (como el atributo Log del ejemplo al principio de esta publicación) simplemente extendiendo la clase InterceptableAttribute. A esta implementación se le pueden agregar tantas propiedades como sean necesarias para definir la meta-data y funcionalidades del aspecto.
  2. Implementar la interface IProcessor y sus dos métodos (si así se requiere) para inyectar la funcionalidad del aspecto en los métodos interceptados.

En próximas publicaciones mostraré ejemplos de como emplear este framework para crear aspectos y consumirlos dentro de un diseño.

Limitaciones

Emplear y diseñar arquitecturas de software empleando el paradigma de AOP no es ni una panacea ni una utopía en .NET; lamentablemente, existe un precio a pagar en tiempo de ejecución y de memoria entre otras limitaciones que debemos tener en cuenta:

  • Desempeño: Empleando diferentes herramientas de medición de desempeño, he encontrado que en el peor caso puede haber un incremento de tiempo de ejecución en cada llamada (invocación a método interceptado) de cerca de 0,05 microsegundos. Esto quiere decir que se necesita cerca de unas 20000 llamadas para perder un segundo, lo cual se podría considerar no tan grave. Sin embargo, en aplicativos donde el tiempo y el desempeño sea primordial… quizás el enfoque de AOP no sea el más apropiado.
  • Memoria: El uso de contextos propios, así como de sinks y en general cualquier objeto que extienda de la clase ContextBoundObjects consume más memoria que objetos “normales”. Hoy día el recurso de memoria no suele ser considerado como un recurso costoso o valioso, pero siempre es importante tenerlo en cuenta.
  • Capacidades: Lamentablemente por diseño y arquitectura del CLR es, a través de esta aproximación e implementación de AOP, absolutamente imposible interceptar métodos o elementos estáticos (static) del código. Esto impone una importante limitación a la hora de diseñar arquitecturas de software, porque significa que o bien descartamos el uso de elementos estáticos, o bien asumimos que su criticidad y necesidad de ser interceptados puede ser ignorada de forma segura, o que su operatividad y la inyección de aspectos puede inferirse y extrapolarse desde sus miembros llamadores no estáticos.

Código Fuente

Por último, haciendo click aquí podrán conseguir una copia del código fuente completo descrito en esta publicación.

Referencias