Skip to content

7. La aplicación [SimuPaie] – versión 3 – arquitectura de tres capas con NHibernate


Lecturas recomendadas: «Lenguaje C# 2008, Capítulo 4: Arquitecturas de tres capas, pruebas NUnit, marco Spring».


7.1. Arquitectura general de la aplicación

La aplicación [SimuPaie] tendrá ahora la siguiente estructura de tres capas:

  • La capa [1-dao] (DAO = Objeto de Acceso a Datos) se encargará del acceso a los datos.
  • La capa [2-métier] se encargará del aspecto de negocio de la aplicación: el cálculo de la nómina.
  • La capa [3-ui] (ui = Interfaz de usuario) se encargará de presentar los datos al usuario y de ejecutar sus consultas. Denominamos [Application] al conjunto de módulos que cumplen esta función. Es el punto de contacto con el usuario.
  • Las tres capas serán independientes gracias al uso de interfaces .NET
  • La integración de las diferentes capas se llevará a cabo mediante Spring IoC

El procesamiento de una solicitud de un cliente se lleva a cabo siguiendo los siguientes pasos:

  1. el cliente envía una solicitud a la aplicación.
  2. La aplicación procesa esta solicitud. Para ello, puede necesitar la ayuda de la capa [métier], la cual a su vez puede necesitar la capa [dao] si es necesario intercambiar datos con la base de datos.
  3. La aplicación recibe una respuesta de la capa [métier]. En función de esta, envía la vista (= la respuesta) adecuada al cliente.

Tomemos como ejemplo el cálculo del salario de una niñera. Esto requerirá varios pasos:

  1. La capa [ui] deberá solicitar al usuario
    • la identidad de la persona para la que se desea calcular el salario
    • el número de días trabajados por esta persona
    • el número de horas trabajadas
  1. Para ello, deberá mostrarle al usuario la lista de personas (apellido, nombre, SS) que figuran en la tabla [EMPLOYES] para que el usuario elija a una de ellas. La capa [ui] utilizará la ruta [2, 3, 4, 5, 6, 7] para obtenerlos. La operación [2] es la solicitud de la lista de empleados, y la operación [7] es la respuesta a dicha solicitud. Una vez hecho esto, la capa [ui] puede presentar la lista de empleados al usuario mediante [8].
  2. El usuario transmitirá a la capa [ui] el número de días trabajados, así como el número de horas trabajadas. Esta es la operación [1] mencionada anteriormente. Durante esta etapa, el usuario solo interactúa con la capa [ui]. Es esta la que, entre otras cosas, verificará la validez de los datos ingresados. Una vez hecho esto, el usuario solicitará el cálculo de la nómina.
  3. La capa [ui] solicitará a la capa de negocio que realice este cálculo. Para ello, le transmitirá los datos que recibió del usuario. Se trata de la operación [2].
  4. La capa [metier] necesita cierta información para llevar a cabo su trabajo:
    • información más completa sobre la persona (dirección, índice, etc.)
    • las prestaciones relacionadas con su índice
    • las tasas de las diferentes cotizaciones sociales que se deben deducir del salario bruto

Solicitará esta información a la capa [dao] mediante la ruta [3, 4, 5, 6]. [3] es la solicitud inicial y [6] la respuesta a dicha solicitud.

  1. Al contar con todos los datos que necesitaba, la capa [metier] calcula la nómina de la persona seleccionada por el usuario.
  2. La capa [metier] ahora puede responder a la solicitud de la capa [ui] realizada en (d). Esta es la ruta [7].
  3. La capa [ui] dará formato a estos resultados para presentárselos al usuario de manera adecuada y, posteriormente, mostrarlos. Esa es la ruta [8].
  4. Es posible que estos resultados deban almacenarse en un archivo o en una base de datos. Esto puede hacerse de manera automática. En ese caso, después de la operación (f), la capa [metier] le pedirá a la capa [dao] que guarde los resultados. Esta será la ruta [3, 4, 5, 6]. Esto también puede realizarse a petición del usuario. En ese caso, se utilizará la ruta [1-8] en el ciclo de solicitud-respuesta.

En esta descripción se observa que una capa utiliza los recursos de la capa que se encuentra a su derecha, nunca de la que está a su izquierda.

Nuestra primera implementación de esta arquitectura de tres capas será una aplicación ASP.NET, en la que

  • las capas [dao] y [metier] serán implementadas por DLL
  • la capa [ui] será implementada por el formulario web de la versión 1 (véase el párrafo 4.2.1).

Comenzamos por implementar la capa [dao] con el marco NHibernate.

7.2. La capa [dao] de acceso a los datos

7.2.1. El proyecto de Visual Studio C# de la capa [dao]

El proyecto de Visual Studio de la capa [dao] es el siguiente:

  • en [1], el proyecto en su totalidad
  • en [2], las diferentes clases del proyecto
  • en [3], las referencias del proyecto.
  • en [4], una carpeta [lib] en la que se han reunido los DLL necesarios para los distintos proyectos que vendrán a continuación

En las referencias [3] del proyecto, se encuentran los siguientes DLL:

  • NHibernate: para el ORM NHibernate
  • MySql.Data: el controlador ADO.NET del SGBD MySQL
  • Spring.Core: para el marco Spring
  • log4net: una biblioteca de registros
  • nunit.framework: una biblioteca de pruebas unitarias

Estas referencias se han tomado de la carpeta [lib] [4]. Se debe asegurar que todas estas referencias tengan la propiedad «Copia local» establecida en «True» [5]:

7.2.2. Las entidades de la capa [dao]

   

Las entidades (objetos) necesarias para la capa [dao] se han reunido en la carpeta [entites] del proyecto. Algunas ya las conocemos: [Cotisations], descrita en el párrafo 6.3.2.1; [Employe], descrita en el párrafo 6.3.2.3; y [Indemnites], descrita en el párrafo 6.3.2.2. Todas ellas se encuentran en el espacio de nombres [Pam.Dao.Entites].

La clase [Employe] evoluciona de la siguiente manera:


namespace Pam.Dao.Entites {
    public class Employe {
        // propiedades automáticas
        public virtual int Id { get; set; }
        public virtual int Version { get; set; }
        public virtual string SS { get; set; }
        public virtual string Nom { get; set; }
        public virtual string Prenom { get; set; }
        public virtual string Adresse { get; set; }
        public virtual string Ville { get; set; }
        public virtual string CodePostal { get; set; }
        public virtual Indemnites Indemnites { get; set; }

        // constructores
        public Employe() {
        }

        // ToString
        public override string ToString() {
            return string.Format("[{0},{1},{2},{3},{4},{5},{6}]", SS, Nom, Prenom, Adresse, Ville, CodePostal, Indemnites);
        }
    }
}

7.2.3. La clase [PamException]

La capa [dao] se encarga de intercambiar datos con una fuente externa. Este intercambio puede fallar. Por ejemplo, si la información se solicita a un servicio remoto en Internet, su obtención fallará ante cualquier falla en la red. Ante este tipo de errores, es habitual en Java lanzar una excepción. Si la excepción no es de tipo [RunTimeException] o un derivado, se debe indicar en la firma del método que este lanza (throws) una excepción. En .NET, todas las excepciones son no controladas, c.a.d. equivalentes al tipo [RunTimeException] de Java. Por lo tanto, no es necesario declarar que los métodos [GetAllIdentitesEmployes, GetEmploye, GetCotisations] pueden lanzar una excepción.

Sin embargo, es interesante poder diferenciar las excepciones entre sí, ya que su manejo puede variar. De este modo, el código que maneja diversos tipos de excepciones puede escribirse de la siguiente manera:

try{
    ... code pouvant générer divers types d'exceptions
}catch (Exception1 ex1){
...on gère un type d'exceptions
}catch (Exception2 ex2){
...on gère un autre type d'exceptions
}finally{
...
}

Por lo tanto, creamos un tipo de excepciones para la capa [dao] de nuestra aplicación. Se trata del siguiente tipo [PamException]:


using System;
namespace Pam.Dao.Entites {

    public class PamException : Exception {

        // el código de error 
        public int Code { get; set; }

        // constructores 
        public PamException() {
        }

        public PamException(int Code)
            : base() {
            this.Code = Code;
        }

        public PamException(string message, int Code)
            : base(message) {
            this.Code = Code;
        }

        public PamException(string message, Exception ex, int Code)
            : base(message, ex) {
            this.Code = Code;
        }
    }
}
  • línea 2: la clase pertenece al espacio de nombres [Pam.Dao.Entites]
  • línea 4: la clase deriva de la clase [Exception]
  • línea 7: tiene una propiedad pública [Code] que es un código de error
  • En nuestra capa [dao] utilizaremos dos tipos de constructores:
    • el de las líneas 18-21, que se puede utilizar como se muestra a continuación:
throw new PamException("Problème d'accès aux données",5);
  • (continuación)
    • o el de las líneas 23 a 26, destinado a reportar una excepción que ya se ha producido, encapsulándola en una excepción de tipo [PamException]:
try{
....
}catch (IOException ex){
     // encapsulamos la excepción
    throw new PamException("Problème d'accès aux données",ex,10);
}

Este segundo método tiene la ventaja de no perder la información que pueda contener la primera excepción.

7.2.4. Los archivos de tablas de mapeo <--> clases de NHibernate

Volvamos a la arquitectura de la aplicación:

En la lectura, el marco NHibernate utiliza datos de la base de datos y los transforma en objetos cuyas clases acabamos de presentar. En la escritura, hace lo contrario: a partir de objetos, crea, actualiza y elimina filas en las tablas de la base de datos. Ya se han presentado los archivos que realizan la transformación tablas <--> clases:

   
  • el archivo [Cotisations.hbm.xml], presentado en el párrafo 6.3.2.1, establece la correspondencia entre la tabla [COTISATIONS] y la clase [Cotisations]

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-nhibernate">
    <class name="Cotisations" table="COTISATIONS">
        <id name="Id" column="ID">
            <generator class="native" />
        </id>
        <version name="Version" column="VERSION"/>
        <property name="CsgRds" column="CSGRDS" not-null="true"/>
        <property name="Csgd" column="CSGD" not-null="true"/>
        <property name="Retraite" column="RETRAITE" not-null="true"/>
        <property name="Secu" column="SECU" not-null="true"/>
    </class>
</hibernate-mapping>
  • El archivo [Employe.hbm.xml], presentado en el párrafo 6.3.2.3, establece la correspondencia entre la tabla [EMPLOYES] y la clase [Employe]

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-nhibernate">
    <class name="Employe" table="EMPLOYES">
        <id name="Id" column="ID">
            <generator class="native" />
        </id>
        <version name="Version" column="VERSION"/>
        <property name="SS" column="SS" length="15" not-null="true" unique="true"/>
        <property name="Nom" column="NOM" length="30" not-null="true"/>
        <property name="Prenom" column="PRENOM" length="20" not-null="true"/>
        <property name="Adresse" column="ADRESSE" length="50" not-null="true" />
        <property name="Ville" column="VILLE" length="30" not-null="true"/>
        <property name="CodePostal" column="CP" length="5" not-null="true"/>
        <many-to-one name="Indemnites" column="INDEMNITE_ID" cascade="save-update" lazy="false"/>
    </class>
</hibernate-mapping>
  • El archivo [Indemnites.hbm.xml], presentado en el párrafo 6.3.2.2, establece la correspondencia entre la tabla [INDEMNITES] y la clase [Indemnites]

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-nhibernate">
    <class name="Indemnites" table="INDEMNITES">
        <id name="Id" column="ID">
            <generator class="native" />
        </id>
        <version name="Version" column="VERSION"/>
        <property name="Indice" column="INDICE" not-null="true" unique="true"/>
        <property name="BaseHeure" column="BASE_HEURE" not-null="true"/>
        <property name="EntretienJour" column="ENTRETIEN_JOUR" not-null="true"/>
        <property name="RepasJour" column="REPAS_JOUR" not-null="true" />
        <property name="IndemnitesCp" column="INDEMNITES_CP" not-null="true"/>
    </class>
</hibernate-mapping>

Cabe señalar que en la etiqueta <hibernate-mapping> de estos archivos (línea 2), se encuentran los siguientes atributos:

  • namespace: Pam.Dao.Entites. Las clases [Cotisations], [Employe] y [Indemnites] deben encontrarse en este espacio de nombres.
  • assembly: pam-dao-nhibernate. Los archivos de mapeo [*.hbm.xml] deben estar encapsulados en un DLL llamado [pam-dao-nhibernate]. Para lograr este resultado, el proyecto de C# se configura de la siguiente manera:
  • en [1], el ensamblado del proyecto se llama [pam-dao-nhibernate]
  • en [2], los archivos de mapeo [*.hbm.xml] se integran en el ensamblado del proyecto como [3]

7.2.5. La interfaz [IPamDao] de la capa [dao]

Volvamos a la arquitectura de nuestra aplicación:

En casos sencillos, podemos partir de la capa [metier] para descubrir las interfaces de la aplicación. Para funcionar, necesita datos:

  • que ya están disponibles en archivos, bases de datos o a través de la red. Estos datos los proporciona la capa [dao].
  • aún no están disponibles. En ese caso, los proporciona la capa [ui], que los obtiene del usuario de la aplicación.

¿Qué interfaz debe ofrecer la capa [dao] a la capa [metier]? ¿Cuáles son las interacciones posibles entre estas dos capas? La capa [dao] debe proporcionar los siguientes datos a la capa [metier]:

  • la lista de cuidadoras infantiles para que el usuario pueda elegir una en particular
  • información completa sobre la persona elegida (dirección, índice, etc.)
  • las prestaciones relacionadas con el índice de la persona
  • las tasas de las diferentes cotizaciones sociales

De hecho, esta información se conoce antes del cálculo de la nómina y, por lo tanto, puede almacenarse. En la dirección [metier] -> [dao], la capa [metier] puede solicitar a la capa [dao] que registre el resultado del cálculo de la nómina. No lo haremos aquí.

Con esta información, podríamos intentar una primera definición de la interfaz de la capa [dao]:


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        // lista de todas las identidades de los empleados 
        Employe[] GetAllIdentitesEmployes();
        // un empleado específico con sus prestaciones 
        Employe GetEmploye(string ss);
        // lista de todas las cotizaciones 
        Cotisations GetCotisations();
    }
}
  • línea 1: se importa el espacio de nombres de las entidades de la capa [dao].
  • línea 3: la capa [dao] se encuentra en el espacio de nombres [Pam.Dao.Service]. Los elementos del espacio de nombres [Pam.Dao.Entites] pueden crearse en varias instancias. Los elementos del espacio de nombres [Pam.Dao.Service] se crean en una sola instancia (singleton). Esto es lo que justificó la elección de los nombres de los espacios de nombres.
  • línea 4: la interfaz se llama [IPamDao]. Define tres métodos:
    • línea 6: [GetAllIdentitesEmployes] devuelve un array de objetos de tipo [Employe] que representa la lista de cuidadoras infantiles en un formato simplificado (apellido, nombre, SS).
    • línea 8, [GetEmploye] devuelve un objeto [Employe]: el empleado cuyo número de seguro social se pasó como parámetro al método, junto con las prestaciones relacionadas con su índice.
    • Línea 10: [GetCotisations] devuelve el objeto [Cotisations], que encapsula las tasas de las diferentes cotizaciones sociales que se deben deducir del salario bruto.

7.3. Implementación y pruebas de la capa [dao]

7.3.1. El proyecto de Visual Studio

El proyecto de Visual Studio ya se ha presentado. Recordemos:

  • en [1], el proyecto en su totalidad
  • en [2], las diferentes clases del proyecto. La carpeta [entites] contiene las entidades manipuladas por la capa [dao], así como los archivos de mapeo NHibernate. La carpeta [service] contiene la interfaz [IPamDao] y su implementación [PamDaoNHibernate]. La carpeta [tests] contiene una prueba de consola [Main.cs] y una prueba unitaria [NUnit.cs].
  • En [3], las referencias del proyecto.

7.3.2. El programa de prueba de consola [Main.cs]

El programa de prueba [Main.cs] se ejecuta en la siguiente arquitectura:

Se encarga de probar los métodos de la interfaz [IPamDao]. Un ejemplo básico podría ser el siguiente:


using System;
using Pam.Dao.Entites;
using Pam.Dao.Service;
using Spring.Context.Support;

namespace Pam.Dao.Tests {
    public class MainPamDaoTests {
        public static void Main() {
            try {
                // instanciación de la capa [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // lista de las identidades de los empleados 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // un empleado con sus prestaciones 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // lista de cotizaciones 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // visualización de excepción 
                Console.WriteLine(ex.ToString());
            }
            //pausa 
            Console.ReadLine();
        }
    }
}
  • línea 11: se le solicita a Spring una referencia en la capa [dao].
  • líneas 13-15: prueba del método [GetAllIdentitesEmployes] de la interfaz [IPamDao]
  • línea 18: prueba del método [GetEmploye] de la interfaz [IPamDao]
  • línea 21: prueba del método [GetCotisations] de la interfaz [IPamDao]

Spring, NHibernate y log4net se configuran mediante el siguiente archivo [App.config] :


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- secciones de configuración -->
    <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
        <sectionGroup name="spring">
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
        </sectionGroup>
        <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
    </configSections>


    <!-- configuración de Spring -->
    <spring>
        <context>
            <resource uri="config://spring/objects" />
        </context>
        <objects xmlns="http://www.springframework.net">
            <object id="pamdao" type="Pam.Dao.Service.PamDaoNHibernate, pam-dao-nhibernate" init-method="init" destroy-method="destroy"/>
        </objects>
    </spring>

    <!-- configuración NHibernate -->
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
        <session-factory>
            <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
            <property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
            <property name="dialect">NHibernate.Dialect.MySQLDialect</property>
            <property name="connection.connection_string">
                Server=localhost;Database=dbpam_nhibernate;Uid=root;Pwd=;
            </property>
            <property name="show_sql">false</property>
            <mapping assembly="pam-dao-nhibernate"/>
        </session-factory>
    </hibernate-configuration>

    <!-- Esta sección contiene los ajustes de configuración de log4net -->
    <!-- NOTE IMPORTANTE: los registros no están activos de forma predeterminada. Deben activarse mediante programación
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
    <log4net>
    ...
    </log4net>

</configuration>

La configuración de NHibernate (línea 10, líneas 25-36) se explicó en el párrafo 6.3.1. Cabe destacar la línea 34, que indica que los archivos de mapeo se encuentran en el ensamblado [pam-dao-nhibernate]. Este es el ensamblado del proyecto.

La configuración de Spring se realiza en las líneas 6 a 9 y 15 a 22. La línea 20 define el objeto [pamdao] utilizado por el programa de consola [Main.cs]. La etiqueta <object> tiene aquí los siguientes atributos:

  • type: establece la clase que se va a instanciar. La clase [PamDaoNHibernate] es la que implementa la interfaz [IPamDao]. Se encuentra en el archivo DLL del proyecto.
  • init-method: el método de la clase [PamDaoNHibernate] que se debe ejecutar después de la instanciación de la clase
  • destroy-method: el método de la clase [PamDaoNHibernate] que se debe ejecutar cuando se destruye el contenedor Spring al finalizar la ejecución del proyecto.

La ejecución realizada con la base de datos descrita en el párrafo 6.2 arroja el siguiente resultado en la consola:

1
2
3
4
5
6
[254104940426058,Jouveinal,Marie,,,,]
[260124402111742,Laverti,Justine,,,,]
------------------------------------
[254104940426058,Jouveinal,Marie,5 rue des oiseaux,St Corentin,49203,[2, 2,1, 2,1, 3,1, 15]]
------------------------------------
[3,49,6,15,9,39,7,88]
  • líneas 1-2: los 2 empleados de tipo [Employe] con la única información [SS, Nom, Prenom]
  • línea 4: el empleado de tipo [Employe] con el número de seguro social [254104940426058]
  • línea 5: las tasas de cotización

7.3.3. Asiento de la clase [PamDaoNHibernate]

La interfaz [IPamDao] implementada por la capa [dao] es la siguiente:


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        // lista de todas las identidades de los empleados 
        Employe[] GetAllIdentitesEmployes();
        //: un empleado específico con sus prestaciones 
        Employe GetEmploye(string ss);
        //: lista de todas las cotizaciones 
        Cotisations GetCotisations();
    }
}

Pregunta: Escriba el código de la clase [PamDaoNHibernate] que implemente la interfaz [IPamDao] anterior utilizando el marco NHibernate configurado tal como se presentó anteriormente. También se implementarán los métodos init y destroy ejecutados por Spring. El método init creará la clase SessionFactory, de la cual se obtendrán los objetos Session. El método destroy cerrará este SessionFactory. Nos basaremos en los ejemplos del párrafo 6.5.


Restricciones:

Se supondrá que ciertos datos solicitados a la capa [dao] pueden caber por completo en la memoria. Así, para mejorar el rendimiento, la clase [PamDaoNHibernate] almacenará:

  • la tabla [EMPLOYES] en forma de (SS, NOM, PRENOM) requerida por el método [GetAllIdentitesEmployes] en forma de una matriz de objetos de tipo [Employe]
  • la tabla [COTISATIONS] en forma de un único objeto de tipo [Cotisations]

Esto se hará en el método [init] de la clase. El esqueleto de la clase [PamDaoNHibernate] podría ser el siguiente:


using System;
...

namespace Pam.Dao.Service {
    class PamDaoNHibernate : IPamDao {
        // campos privados 
        private Cotisations cotisations;
        private Employe[] employes;
        private ISessionFactory sessionFactory = null;

        // inicialización 
        public void init() {
            try {
                // inicialización de fábrica
                sessionFactory = new Configuration().Configure().BuildSessionFactory();
                // se recuperan las tasas de cotización y los empleados para almacenarlos en caché 
.......................
        }

        // cierre SessionFactory
        public void destroy() {
            if (sessionFactory != null) {
                sessionFactory.Close();
            }
        }

        // lista de todas las identidades de los empleados 
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }

        // un empleado en particular con sus prestaciones 
        public Employe GetEmploye(string ss) {
................................
        }

        // lista de cotizaciones 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}

7.3.4. Pruebas unitarias con NUnit


Lecturas recomendadas: «Lenguaje C# 2008, Capítulo 4: Arquitecturas de tres capas, pruebas NUnit, marco Spring».


La prueba anterior había sido visual: se verificaba en la pantalla que se obtuvieran los resultados esperados. Este método es insuficiente en el ámbito profesional. Las pruebas siempre deben automatizarse al máximo y tener como objetivo no requerir ninguna intervención humana. De hecho, el ser humano está sujeto a la fatiga y su capacidad para verificar pruebas se va mermando a lo largo del día. La herramienta [NUnit] ayuda a llevar a cabo esta automatización. Está disponible en el URL [http://www.nunit.org/].

El proyecto de Visual Studio de la capa [dao] evolucionará de la siguiente manera:

  • a [1]; el programa de prueba [NUnit.cs]
  • a [2,3]; el proyecto generará un archivo DLL denominado [pam-dao-nhibernate.dll]
  • en [4], la referencia a DLL del marco NUnit: [nunit.framework.dll]
  • en [5], la clase [Main.cs] no se incluirá en DLL [pam-dao-nhibernate]
  • en [6], la clase [NUnit.cs] se incluirá en la clase DLL [pam-dao-nhibernate]

La clase de prueba NUnit es la siguiente:


using System.Collections;
using NUnit.Framework;
using Pam.Dao.Service;
using Pam.Dao.Entites;
using Spring.Objects.Factory.Xml;
using Spring.Core.IO;
using Spring.Context.Support;

namespace Pam.Dao.Tests {

    [TestFixture]
    public class NunitPamDao : AssertionHelper {
        // la capa [dao] para probar 
        private IPamDao pamDao = null;

        // fabricante 
        public NunitPamDao() {
            // instanciación de la capa [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }

        // inicialización 
        [SetUp]
        public void Init() {

        }

        [Test]
        public void GetAllIdentitesEmployes() {
            // verificación del número de empleados 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetCotisations() {
            // verificación de la tasa de cotizaciones 
            Cotisations cotisations = pamDao.GetCotisations();
            Expect(3.49, EqualTo(cotisations.CsgRds).Within(1E-06));
            Expect(6.15, EqualTo(cotisations.Csgd).Within(1E-06));
            Expect(9.39, EqualTo(cotisations.Secu).Within(1E-06));
            Expect(7.88, EqualTo(cotisations.Retraite).Within(1E-06));
        }

        [Test]
        public void GetEmployeIdemnites() {
            // verificación de personas 
            Employe employe1 = pamDao.GetEmploye("254104940426058");
            Employe employe2 = pamDao.GetEmploye("260124402111742");
            Expect("Jouveinal", EqualTo(employe1.Nom));
            Expect(2.1, EqualTo(employe1.Indemnites.BaseHeure).Within(1E-06));
            Expect("Laverti", EqualTo(employe2.Nom));
            Expect(1.93, EqualTo(employe2.Indemnites.BaseHeure).Within(1E-06));
        }

        [Test]
        public void GetEmployeIdemnites2() {
            // verificación de persona inexistente 
            bool erreur = false;
            try {
                Employe employe1 = pamDao.GetEmploye("xx");
            } catch {
                erreur = true;
            }
            Expect(erreur, True);
        }
    }
}
  • línea 11: la clase tiene el atributo [TestFixture], lo que la convierte en una clase de prueba [NUnit].
  • línea 12: la clase deriva de la clase de utilidad AssertionHelper del marco NUnit (a partir de la versión 2.4.6).
  • línea 14: el campo privado [pamDao] es una instancia de la interfaz de acceso a la capa [dao]. Cabe señalar que el tipo de este campo es una interfaz y no una clase. Esto significa que la instancia [pamDao] solo da acceso a los métodos de la interfaz [IPamDao].
  • Los métodos probados en la clase son aquellos que tienen el atributo [Test]. Para todos estos métodos, el proceso de prueba es el siguiente:
    • en primer lugar, se ejecuta el método con el atributo [SetUp]. Este sirve para preparar los recursos (conexiones de red, conexiones a bases de datos, etc.) necesarios para la prueba.
    • Luego se ejecuta el método que se va a probar
    • y, por último, se ejecuta el método con el atributo [TearDown]. Por lo general, sirve para liberar los recursos utilizados por el método con el atributo [SetUp].
  • En nuestra prueba, no hay recursos que asignar antes de cada prueba y que luego desasignar. Por lo tanto, no necesitamos métodos con los atributos [SetUp] y [TearDown]. A modo de ejemplo, en las líneas 23 a 26 presentamos un método con el atributo [SetUp].
  • líneas 17-20: el constructor de la clase inicializa el campo privado [pamDao] utilizando Spring y [App.config].
  • Líneas 29-32: prueban el método [GetAllIdentitesEmployes]
  • líneas 35-42: prueban el método [GetCotisations]
  • líneas 45-53: prueban el método [GetEmploye]
  • líneas 56-65: prueban el método [GetEmploye] al ocurrir una excepción.

La generación del proyecto crea los archivos DLL y [pam-dao-nhibernate.dll] en la carpeta [bin/Release].

La carpeta [bin/Release] contiene además:

  • los archivos DLL que forman parte de las referencias del proyecto y que tienen el atributo [Copie locale] establecido en verdadero: [Spring.Core, MySql.data, NHibernate, log4net]. Estos DLL van acompañados de copias de los DLL que ellos mismos utilizan:
    • [CastleDynamicProxy, Iesi.Collections] para la herramienta NHibernate
    • [antlr.runtime, Common.Logging] para la herramienta Spring
  • el archivo [pam-dao-nhibernate.dll.config] es una copia del archivo de configuración [App.config]. Es VS el que realiza esta duplicación. Al ejecutarse, se utiliza el archivo [pam-dao-nhibernate.dll.config] y no el [App.config].

Se cargan los archivos DLL y [pam-dao-nhibernate.dll] con la herramienta [NUnit-Gui], versión 2.4.6, y se ejecutan las pruebas:

Image

En el ejemplo anterior, las pruebas se realizaron con éxito.

Trabajo práctico:


  • ejecutar en la máquina las pruebas de la clase [PamDaoNHibernate].
  • utilizar diferentes archivos de configuración [App.config] para utilizar diferentes SGBD (Firebird, MySQL, Postgres, SQL Server)

7.3.5. Generación de l e la DLL de la capa [dao]

Una vez escrita y probada la clase [PamDaoNHibernate], se generará la clase DLL a partir de la capa [dao] de la siguiente manera:

  • [1]; los programas de prueba se excluyen del ensamblaje del proyecto
  • [2,3], configuración del proyecto
  • [4], generación del proyecto
  • el archivo DLL se genera en la carpeta [bin/Release] [5]. Lo agregamos a los archivos DLL que ya se encuentran en la carpeta [lib] [6]:

7.4. La capa de negocio

Volvamos a la arquitectura general de la aplicación [SimuPaie]:

Ahora damos por hecho que la capa [dao] está lista y que se ha encapsulado en DLL [pam-dao-nhibernate.dll]. Ahora nos enfocamos en la capa [metier]. Es esta la que implementa las reglas de negocio, en este caso, las reglas para calcular un salario.

7.4.1. El proyecto de Visual Studio « » de la capa [metier]

El proyecto de Visual Studio de la capa de negocio podría tener el siguiente aspecto:

  • en [1], el proyecto completo configurado por el archivo [App.config]
  • en [2], la capa [metier] está formada por las dos carpetas [entites, service]. La carpeta [tests] contiene un programa de prueba de consola (Main.cs) y un programa de prueba NUnit (NUnit.cs).
  • En [3] se encuentran las referencias utilizadas por el proyecto. Cabe destacar las referencias DLL y [pam-dao-nhibernate] de la capa [dao] analizada anteriormente.

7.4.2. La interfaz [IPamMetier] de la capa [metier]

Volvamos a la arquitectura general de la aplicación:

¿Qué interfaz debe ofrecer la capa [metier] a la capa [ui]? ¿Cuáles son las interacciones posibles entre estas dos capas? Recordemos la interfaz web que se le presentará al usuario:

  1. Al mostrar el formulario por primera vez, debe aparecer en [1] la lista de empleados. Basta con una lista simplificada (Apellido, Nombre, SS). El n.º SS es necesario para acceder a la información adicional sobre el empleado seleccionado (información del 6 al 11).
  2. Los datos del 12 al 15 corresponden a las diferentes tasas de cotización.
  3. Los datos del 16 al 19 son las indemnizaciones vinculadas al índice del empleado
  4. Los datos del 20 al 24 son los componentes del salario calculados a partir de las entradas 1 a 3 realizadas por el usuario.

La interfaz [IPamMetier] que la capa [metier] proporciona a la capa [ui] debe cumplir con los requisitos anteriores. Existen numerosas interfaces posibles. Proponemos la siguiente:


using Pam.Dao.Entites;
using Pam.Metier.Entites;

namespace Pam.Metier.Service {
    public interface IPamMetier {
        // lista de todas las identidades de los empleados 
        Employe[] GetAllIdentitesEmployes();

        // ------- cálculo del salario 
        FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
    }
}
  • línea 7: el método que permitirá completar el combo [1]
  • línea 10: el método que permitirá obtener la información de los campos 6 a 24. Estos se han reunido en un objeto de tipo [FeuilleSalaire].

7.4.3. Las entidades de la capa [metier]

El archivo [entites] del proyecto de Visual Studio contiene los objetos manipulados por la clase de negocio: [FeuilleSalaire] y [ElementsSalaire].

La clase [FeuilleSalaire] encapsula la información del 6 al 24 del formulario anterior:


using Pam.Dao.Entites;

namespace Pam.Metier.Entites {

    public class FeuilleSalaire {

        // propiedades automáticas 
        public Employe Employe { get; set; }
        public Cotisations Cotisations { get; set; }
        public ElementsSalaire ElementsSalaire { get; set; }

        // ToString 
        public override string ToString() {
            return string.Format("[{0},{1},{2}", Employe, Cotisations, ElementsSalaire);
        }
    }
}
  • línea 8: la información del 6 al 11 sobre el empleado cuyo salario se calcula y la información del 16 al 19 sobre sus prestaciones. No hay que olvidar aquí que un objeto [Employe] encapsula un objeto [Indemnites] que representa las prestaciones del empleado.
  • línea 9: la información del 12 al 15
  • línea 10: la información del 20 al 24
  • líneas 13-15: el método [ToString]

La clase [ElementsSalaire] encapsula la información de las líneas 20 a 24 del formulario:


namespace Pam.Metier.Entites {
    public class ElementsSalaire {
        // propiedades automáticas 
        public double SalaireBase { get; set; }
        public double CotisationsSociales { get; set; }
        public double IndemnitesEntretien { get; set; }
        public double IndemnitesRepas { get; set; }
        public double SalaireNet { get; set; }


        // ToString 
        public override string ToString() {
            return string.Format("[{0} : {1} : {2} : {3} : {4} ]", SalaireBase, CotisationsSociales, IndemnitesEntretien, IndemnitesRepas, SalaireNet);
        }
    }
}
  • líneas 4-8: los componentes del salario tal como se explican en las reglas de negocio descritas en el párrafo 3.2.
  • línea 4: el salario base del empleado, en función del número de horas trabajadas
  • línea 5: las cotizaciones deducidas de este salario base
  • líneas 6 y 7: las indemnizaciones que se suman al salario base, en función del índice del empleado y del número de días trabajados
  • línea 8: el salario neto a pagar
  • líneas 12-15: el método [ToString] de la clase.

7.4.4. Implementación de la capa [metier]

Vamos a implementar la interfaz [IPamMetier] con dos clases:

  • [AbstractBasePamMetier], que es una clase abstracta en la que implementaremos el acceso a los datos de la interfaz [IPamMetier]. Esta clase tendrá una referencia a la capa [dao].
  • [PamMetier], una clase derivada de [AbstractBasePamMetier], que a su vez implementará las reglas de negocio de la interfaz [IPamMetier]. No tendrá conocimiento de la capa [dao].

La clase [AbstractBasePamMetier] será la siguiente:


using Pam.Dao.Entites;
using Pam.Dao.Service;
using Pam.Metier.Entites;

namespace Pam.Metier.Service {
    public abstract class AbstractBasePamMetier : IPamMetier {

        // el objeto de acceso a los datos 
        public IPamDao PamDao { get; set; }

        // lista de todas las identidades de los empleados 
        public Employe[] GetAllIdentitesEmployes() {
            return PamDao.GetAllIdentitesEmployes();
        }

        // un empleado específico con sus prestaciones 
        protected Employe GetEmploye(string ss) {
            return PamDao.GetEmploye(ss);
        }

        // las cotizaciones 
        protected Cotisations GetCotisations() {
            return PamDao.GetCotisations();
        }

        // el cálculo del salario 
        public abstract FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
    }
}
  • línea 5: la clase pertenece al espacio de nombres [Pam.Metier.Service], al igual que todas las clases e interfaces de la capa [metier].
  • línea 6: la clase es abstracta (atributo abstract) e implementa la interfaz [IPamMetier]
  • línea 9: la clase contiene una referencia a la capa [dao] en forma de una propiedad pública
  • líneas 12-14: implementación del método [GetAllIdentitesEmployes] de la interfaz [IPamMetier]; utiliza el método del mismo nombre de la capa [dao]
  • líneas 17-19: método interno (protegido) [GetEmploye] que invoca el método del mismo nombre de la capa [dao] – declarado como protected para que las clases derivadas puedan acceder a él sin que sea público.
  • líneas 22-24: método interno (protegido) [GetCotisations] que llama al método del mismo nombre de la capa [dao]
  • línea 27: implementación abstracta (atributo abstract) del método [GetSalaire] de la interfaz [IPamMetier].

El cálculo del salario se implementa mediante la siguiente clase [PamMetier]:


using System;
using Pam.Dao.Entites;
using Pam.Metier.Entites;

namespace Pam.Metier.Service {

    public class PamMetier : AbstractBasePamMetier {

        // cálculo del salario 
        public override FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés) {
            // SS: número de empleado SS 
            // HeuresTravaillées: número de horas trabajadas 
            // Días trabajados: número de días trabajados 
            //: se recupera el empleado con sus indemnizaciones 
            ...
            //: se recuperan las distintas tasas de cotización 
            ...
            // se calculan los componentes del salario 
            ...
            // se genera la nómina 
            return ...;
        }
    }
}
  • línea 7: la clase deriva de [AbstractBasePamMetier] y, por lo tanto, implementa la interfaz [IPamMetier]
  • línea 10: el método [GetSalaire] que se debe implementar

Pregunta: escribe el código del método [GetSalaire].


7.4.5. La prueba de consola de la capa [metier]

Recordemos el proyecto de Visual Studio de la capa [metier]:

El programa de prueba [Main] anterior prueba los métodos de la interfaz [IPamMetier]. Un ejemplo básico podría ser el siguiente:


using System;
using Pam.Dao.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;

namespace Pam.Metier.Tests {
    class MainPamMetierTests {
        public static void Main() {
            try {
                // instanciación de la capa [metier]
                IPamMetier pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
                // cálculos de las hojas de pago 
                Console.WriteLine(pamMetier.GetSalaire("260124402111742", 30, 5));
                Console.WriteLine(pamMetier.GetSalaire("254104940426058", 150, 20));
                try {
                    Console.WriteLine(pamMetier.GetSalaire("xx", 150, 20));
                } catch (PamException ex) {
                    Console.WriteLine(string.Format("PamException : {0}", ex.Message));
                }
            } catch (Exception ex) {
                Console.WriteLine(string.Format("Exception : {0}", ex.ToString()));
            }
            // pausa 
            Console.ReadLine();
        }
    }
}
  • línea 11: instanciación por parte de Spring de la capa [metier].
  • líneas 13-14: pruebas del método [GetSalaire] de la interfaz [IPamMetier]
  • líneas 15-22: prueba del método [GetSalaire] cuando se produce una excepción

El programa de prueba utiliza el archivo de configuración [App.config] siguiente:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- secciones de configuración -->
    <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
        <sectionGroup name="spring">
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
        </sectionGroup>
        <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
    </configSections>


    <!-- configuración de Spring -->
    <spring>
        <context>
            <resource uri="config://spring/objects" />
        </context>
        <objects xmlns="http://www.springframework.net">
            <object id="pamdao" type="Pam.Dao.Service.PamDaoNHibernate, pam-dao-nhibernate" init-method="init" destroy-method="destroy"/>
            <object id="pammetier" type="Pam.Metier.Service.PamMetier, pam-metier-dao-nhibernate" >
                <property name="PamDao" ref="pamdao"/>
            </object>
        </objects>
    </spring>

    <!-- configuración NHibernate -->
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
....
    </hibernate-configuration>

    <!-- Esta sección contiene los ajustes de configuración de log4net -->
    <!-- NOTE IMPORTANTE: los registros no están activos de forma predeterminada. Deben activarse mediante programación
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
    <log4net>
...
    </log4net>

</configuration>

Este archivo es idéntico al archivo [App.config] utilizado para el proyecto de la capa [dao] (véase el párrafo 7.3.2), salvo por los siguientes detalles:

  • línea 20: el objeto con id «pamdao» tiene el tipo [Pam.Dao.Service.PamDaoNHibernate] y se encuentra en el ensamblaje [pam-dao-nhibernate]. La capa [dao] es la que se analizó anteriormente.
  • líneas 21-23: el objeto con id «pammetier» tiene el tipo [Pam.Metier.Service.PamMetier] y se encuentra en el ensamblado [pam-metier-dao-nhibernate]. Es necesario configurar el proyecto de la siguiente manera:
 
  • línea 22: el objeto [PamMetier] instanciado por Spring tiene una propiedad pública [PamDao] que es una referencia a la capa [dao]. Esta propiedad se inicializa con la referencia de la capa [dao] creada en la línea 20.

La ejecución realizada con la base de datos descrita en el párrafo 6.2 arroja el siguiente resultado en la consola:

1
2
3
[[260124402111742,Laverti,Justine,La Brûlerie,St Marcel,49014,[1, 1,93, 2, 3, 12]],[3,49,6,15,9,39,7,88],[1, 1,93, 2, 3, 12],[64,85 : 17,45 : 10 : 15 : 72,4 ]
[[254104940426058,Jouveinal,Marie,5 rue des oiseaux,St Corentin,49203,[2, 2,1, 2,1, 3,1, 15]],[3,49,6,15,9,39,7,88],[2, 2,1, 2,1, 3,1, 15],[362,25 : 97,48 : 42: 62 : 368,77 ]
PamException : L'employé de n° ss [xx] n'existe pas
  • líneas 1-2: las dos hojas de pago solicitadas
  • línea 3: la excepción de tipo [PamException] provocada por un empleado inexistente.

7.4.6. Pruebas unitarias de la capa de negocio

La prueba anterior fue visual: verificamos en la pantalla que se obtuvieran los resultados esperados. Ahora pasamos a las pruebas no visuales NUnit.

Volvamos al proyecto de Visual Studio del proyecto [metier]:

  • en [1], el programa de prueba NUnit
  • en [2], la referencia en DLL [nunit.framework]
  • en [3,4], la generación del proyecto producirá el DLL y el [pam-metier-dao-nhibernate.dll].
  • En [5], el archivo [NUnit.cs] se incluirá en el ensamblado [pam-metier-dao-nhibernate.dll], pero no en [Main.cs] ni en [6]

La clase de prueba NUnit es la siguiente:


using NUnit.Framework;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;

namespace Pam.Metier.Tests {

    [TestFixture()]
    public class NunitTestPamMetier : AssertionHelper {

        // la capa [metier] que se debe probar 
        private IPamMetier pamMetier;

        // constructor
        public NunitTestPamMetier() {
            // instanciación de la capa [dao]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        }


        [Test]
        public void GetAllIdentitesEmployes() {
            // verificación del número de empleados 
            Expect(2, EqualTo(pamMetier.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetSalaire1() {
            // cálculo de una nómina 
            FeuilleSalaire feuilleSalaire = pamMetier.GetSalaire("254104940426058", 150, 20);
            // verificaciones 
            Expect(368.77, EqualTo(feuilleSalaire.ElementsSalaire.SalaireNet).Within(1E-06));
            // nómina de un empleado inexistente 
            bool erreur = false;
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (PamException) {
                erreur = true;
            }
            Expect(erreur, True);
        }

    }
}
  • línea 13: el campo privado [pamMetier] es una instancia de la interfaz de acceso a la capa [metier]. Cabe señalar que el tipo de este campo es una interfaz y no una clase. Esto significa que la instancia [PamMetier] solo da acceso a los métodos de la interfaz [IPamMetier].
  • líneas 16-19: el constructor de la clase inicializa el campo privado [pamMetier] utilizando Spring y el archivo de configuración [App.config].
  • líneas 23-26: prueban el método [GetAllIdentitesEmployes]
  • líneas 29-42: prueban el método [GetSalaire]

El proyecto anterior genera los archivos DLL y [pam-metier.dll] en la carpeta [bin/Release].

La carpeta [bin/Release] contiene además:

  • los archivos DLL que forman parte de las referencias del proyecto y que tienen el atributo [Copie locale] establecido en verdadero: [Spring.Core, MySql.data, NHibernate, log4net, pam-dao-nhibernate]. Estos DLL van acompañados de copias de los DLL que ellos mismos utilizan:
    • [CastleDynamicProxy, Iesi.Collections] para la herramienta NHibernate
    • [antlr.runtime, Common.Logging] para la herramienta Spring
  • el archivo [pam-metier-dao-nhibernate.dll.config] es una copia del archivo de configuración [App.config].

Cargamos DLL y [pam-metier-dao-nhibernate.dll] con la herramienta [NUnit-Gui, version 2.4.6] y ejecutamos las pruebas:

Image

En el ejemplo anterior, las pruebas se realizaron con éxito.

Trabajo práctico:


  • implementar en la máquina las pruebas de la clase [PamMetier].
  • utilizar diferentes archivos de configuración App.config para utilizar diferentes SGBD (Firebird, MySQL, Postgres, SQL Server)

7.4.7. Generación del DLL de la capa [metier]

Una vez escrita y probada la clase [PamMetier], se generará la clase DLL a partir de la clase [pam-metier-dao-nhibernate.dll] de la capa [metier], siguiendo el método descrito en el párrafo 7.3.5 Se tendrá cuidado de no incluir en la clase DLL los programas de prueba [Main.cs] y [NUnit.cs]. A continuación, se colocará en la carpeta [lib] de DLL [1].

7.5. La capa [web]

Volvamos a la arquitectura general de la aplicación [SimuPaie]:

Consideramos que las capas [dao] y [métier] están implementadas y encapsuladas en las capas DLL y [pam-dao-nhibernate, pam-metier-dao-nhibernate.dll]. A continuación, describimos la capa web.

7.5.1. El proyecto Visual Web Developer de la capa [web]

  • en [1], el proyecto en su conjunto:
    • [Global.asax]: la clase que se instancia al iniciar la aplicación web y que se encarga de la inicialización de la aplicación
    • [Default.aspx]: la página del formulario web
  • en [2], los DLL necesarios para la aplicación web. Cabe destacar los DLL de las capas [dao] y [metier] creadas anteriormente.

7.5.2. Configuración de la aplicación

El archivo [Web.config], que configura la aplicación, define los mismos datos que el archivo [App.config], el cual configura la capa [metier] analizada anteriormente. Estos deben incluirse en el código pregenerado del archivo [Web.config]:


<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <configSections>
    <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
..........
    </sectionGroup>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
    </sectionGroup>
    <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
  </configSections>

  <!-- Configuración de Spring -->
  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object id="pamdao" type="Pam.Dao.Service.PamDaoNHibernate, pam-dao-nhibernate" init-method="init" destroy-method="destroy"/>
      <object id="pammetier" type="Pam.Metier.Service.PamMetier, pam-metier-dao-nhibernate" >
        <property name="PamDao" ref="pamdao"/>
      </object>
    </objects>
  </spring>

  <!-- Configuración de NHibernate -->
  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
      <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
      <!--
            <property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
            -->
      <property name="dialect">NHibernate.Dialect.MySQLDialect</property>
      <property name="connection.connection_string">
        Server=localhost;Database=dbpam_nhibernate;Uid=root;Pwd=;
      </property>
      <property name="show_sql">false</property>
      <mapping assembly="pam-dao-nhibernate"/>
    </session-factory>
  </hibernate-configuration>

  <!-- Esta sección contiene los ajustes de configuración de log4net -->
  <!-- NOTE IMPORTANTE: los registros no están activos por defecto. Es necesario activarlos mediante un programa
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
  <log4net>
....
  </log4net>

  <appSettings/>
  <connectionStrings/>

  <system.web>
....
....

</configuration>

En las líneas 9-12, 18-28 y 31-44 se encuentra la configuración de Spring y NHibernate descrita en el archivo [App.config] de la capa [metier] (véase el párrafo 7.4.5).

Global.asax.cs


using System;
using Pam.Dao.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;

namespace pam_v3
{
  public class Global : System.Web.HttpApplication
  {
    // --- datos estáticos de la aplicación ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;
    public static string Msg;
    public static bool Erreur = false;

    // inicio de la aplicación
    public void Application_Start(object sender, EventArgs e)
    {
      // procesamiento del archivo de configuración
      try
      {
        // instanciación de la capa [metier]
        PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        // lista simplificada de empleados
        Employes = PamMetier.GetAllIdentitesEmployes();
        // se ha realizado con éxito
        Msg = "Base chargée...";
      }
      catch (Exception ex)
      {
        // se registra el error
        Msg = string.Format("L'erreur suivante s'est produite lors de l'accès à la base de données : {0}", ex);
        Erreur = true;
      }
    }
  }
}

Cabe recordar que:

  • la clase [Global.asax.cs] se instancia al iniciar la aplicación y que esta instancia es accesible para todas las consultas de todos los usuarios. Por lo tanto, los campos estáticos de las líneas 11 a 14 se comparten entre todos los usuarios.
  • el método [Application_Start] se ejecuta una sola vez después de la instanciación de la clase. Este es el método en el que, por lo general, se realiza la inicialización de la aplicación.

Los datos compartidos por todos los usuarios son los siguientes:

  • línea 11: el array de objetos de tipo [Employe] que almacenará la lista simplificada (SS, NOM, PRENOM) de todos los empleados
  • línea 12: una referencia a la capa [metier] encapsulada en DLL [pam-metier-dao-nhibernate.dll]
  • línea 13: un mensaje que indica cómo terminó la inicialización (correctamente o con error)
  • línea 14: un valor booleano que indica si la inicialización terminó con un error o no.

En [Application_Start]:

  • línea 23: Spring instancia las capas [metier] y [dao] y devuelve una referencia a la capa [metier]. Esta se almacena en el campo estático [PamMetier] de la línea 12.
  • línea 25: se solicita la tabla de empleados a la capa [metier]
  • línea 27: el mensaje en caso de éxito
  • línea 32: el mensaje en caso de error

7.5.3. El formulario [Default.aspx]

El formulario es el de la versión 2.

Image


Pregunta: Basándote en el código C# de la página [Default.aspx.cs] de la versión 2, escribe el código [Default.aspx.cs] de la versión 3. La única diferencia está en el cálculo del salario. Mientras que en la versión 2 se utilizaba el método API ADO.NET para recuperar información de la base de datos, aquí se utilizará el método GetSalaire de la capa [metier].


Ejercicio práctico:


  • implementar en la máquina la aplicación web anterior
  • utilizar diferentes archivos de configuración [Web.config] para emplear distintos SGBD (Firebird, MySQL, Postgres, SQL Server)