Skip to content

7. La aplicación [SimuPaie] – version 3 – arquitectura de 3 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=Data Access Object) se encargará del acceso a los datos.
  • La capa [2-métier] se encargará del aspecto funcional de la aplicación, el cálculo de la nómina.
  • La capa [3-ui] (ui = Interfaz de usuario) se encargará de la presentación de los datos al usuario y de la ejecución de sus consultas. Denominamos [Application] al conjunto de módulos que garantizan esta función. Es el interlocutor del 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 realiza una solicitud a la aplicación.
  2. La aplicación procesa esta solicitud. Para ello, puede necesitar la ayuda de la capa [métier], que 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 de la nómina de una cuidadora infantil. Este requerirá varios pasos:

  1. La capa [ui] deberá solicitar al usuario
    • la identidad de la persona para la que se quiere calcular la nómina
    • el número de días trabajados por esta
    • el número de horas trabajadas
  1. Para ello, deberá mostrarle la lista de personas (nombre, apellidos, SS) presentes 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 obtenerlas. La operación [2] es la solicitud de la lista de empleados, la operación [7] 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. Se trata de la operación [1] mencionada anteriormente. Durante esta etapa, el usuario solo interactúa con la capa [ui]. Es esta la que verificará, en particular, la validez de los datos introducidos. 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 ha recibido 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
    • los tipos de las diferentes cotizaciones sociales que deben deducirse 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. Una vez que dispone de todos los datos que necesitaba, la capa [metier] calcula la nómina de la persona seleccionada por el usuario.
  2. La capa [metier] puede ahora responder a la solicitud de la capa [ui] realizada en (d). Esta es la ruta [7].
  3. La capa [ui] formateará estos resultados para presentárselos al usuario de forma adecuada y, a continuación, los mostrará. Se trata de la ruta [8].
  4. Es posible que estos resultados deban almacenarse en un archivo o en una base de datos. Esto puede hacerse de forma automática. En este caso, tras la operación (f), la capa [metier] solicitará a la capa [dao] que guarde los resultados. Se utilizará la ruta [3, 4, 5, 6]. Esto también puede hacerse a petición del usuario. 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 situada a su derecha, nunca los 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] se implementará mediante el formulario web de version 1 (véase el apartado 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 diferentes 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 framework Spring
  • log4net: una biblioteca de registros
  • nunit.framework: una biblioteca de pruebas unitarias

Estas referencias se han tomado de la carpeta [lib] [4]. Se asegurará de que todas estas referencias tengan su propiedad «Copia local» 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 nos son conocidas: [Cotisations] descrita en el apartado 6.3.2.1, [Employe] descrita en el apartado 6.3.2.3, [Indemnites] descrita en el apartado 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 fallo de la red. Ante este tipo de errores, lo habitual en Java es lanzar una excepción. Si la excepción no es de tipo [RunTimeException] o derivado, hay que 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, resulta interesante poder diferenciar las excepciones entre sí, ya que su tratamiento puede variar. Así, el código que gestiona 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-26, destinado a propagar una excepción ya ocurrida 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 lectura, el marco NHibernate extrae datos de la base de datos y los transforma en objetos cuyas clases acabamos de presentar. En 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 garantizan la transformación tablas <--> clases:

   
  • el archivo [Cotisations.hbm.xml] presentado en el apartado 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 apartado 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 apartado 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 encapsularse en un DLL denominado [pam-dao-nhibernate]. Para obtener este resultado, el proyecto 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 [3] en el ensamblado del proyecto

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

Volvamos a la arquitectura de nuestra aplicación:

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

  • ya disponibles en archivos, bases de datos o a través de la red. Estos son proporcionados por la capa [dao].
  • aún no 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 concreto
  • 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 prestaciones sociales

De hecho, esta información se conoce antes del cálculo de la nómina y, por lo tanto, puede almacenarse. En el sentido [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 concreto con sus prestaciones 
        Employe GetEmploye(string ss);
        // lista de todos los cotisations 
        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 única instancia (singleton). Esto es lo que ha justificado 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 una matriz de objetos de tipo [Employe] que representa la lista de cuidadoras infantiles en un formato simplificado (apellidos, nombre, SS).
    • línea 8, [GetEmploye] devuelve un objeto [Employe]: el empleado cuyo número de la Seguridad Social se ha pasado como parámetro al método, junto con las prestaciones relacionadas con su índice.
    • Línea 10, [GetCotisations] devuelve el objeto [Cotisations] que encapsula los tipos de las diferentes cotizaciones sociales cotisations que deben deducirse 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 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 cotisations 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // visualización de excepciones 
                Console.WriteLine(ex.ToString());
            }
            //pausa 
            Console.ReadLine();
        }
    }
}
  • línea 11: se solicita a Spring una referencia sobre 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 por defecto. 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 ha explicado en el apartado 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-9 y 15-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. Se trata de la clase [PamDaoNHibernate], que implementa la interfaz [IPamDao]. Se encuentra en el archivo DLL [pam-dao-nhibernate] del proyecto.
  • init-method: el método de la clase [PamDaoNHibernate] que se ejecutará tras la instanciación de la clase
  • destroy-method: el método de la clase [PamDaoNHibernate] que se ejecutará cuando se destruya el contenedor Spring al final de la ejecución del proyecto.

La ejecución realizada con la base de datos descrita en el apartado 6.2 da 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.º de la Seguridad Social [254104940426058]
  • línea 5: las tasas de cotisations

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 concreto con sus prestaciones 
        Employe GetEmploye(string ss);
        //: lista de todas las cotisations 
        Cotisations GetCotisations();
    }
}

Pregunta: escriba el código de la clase [PamDaoNHibernate] que implementa la interfaz [IPamDao] anterior utilizando el marco NHibernate configurado tal y como se ha presentado anteriormente. También se implementarán los métodos init y destroy ejecutados por Spring. El método init creará la SessionFactory de la que se obtendrán los objetos Session. El método destroy cerrará esta SessionFactory. Nos basaremos en los ejemplos del apartado 6.5.


Restricciones:

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

  • 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 tarifas de cotisations y los empleados para almacenarlas 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 concreto con sus prestaciones 
        public Employe GetEmploye(string ss) {
................................
        }

        // lista de cotisations 
        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, framework Spring».


La prueba anterior era visual: se comprobaba en pantalla que se obtenían los resultados esperados. Este método es insuficiente en el ámbito profesional. Las pruebas deben automatizarse siempre 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]
  • en [2,3], el proyecto generará un 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 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] que se va a probar 
        private IPamDao pamDao = null;

        // constructor 
        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 tasa de cotisations 
            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 utilidades AssertionHelper del marco NUnit (a partir de la versión 2.4.6 de version).
  • 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 métodos, concretamente a los 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.
    • A continuación, se ejecuta el método que se va a probar
    • y, por último, se ejecuta el método con el atributo [TearDown]. Este sirve generalmente para liberar los recursos movilizados 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, hemos presentado, en las líneas 23-26, 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] en caso de excepción.

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

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

  • los DLL que forman parte de las referencias del proyecto y que tienen el atributo [Copie locale] 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. En la ejecución 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], version 2.4.6 y se ejecutan las pruebas:

En el ejemplo anterior, las pruebas se han superado.

Trabajo práctico:


  • ejecutar en el equipo 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
  • se genera DLL en la carpeta [bin/Release] [5]. Lo añadimos a los DLL ya presentes en la carpeta [lib] [6]:

7.4. La capa de negocio

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

Consideramos ahora que la capa [dao] está implementada y que ha sido encapsulada en DLL [pam-dao-nhibernate.dll]. Ahora nos centramos en la capa [metier]. Es esta la que implementa las reglas de negocio, en este caso las reglas de cálculo de 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 conjunto del proyecto 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 DLL y [pam-dao-nhibernate] de la capa [dao] estudiada 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 presentará al usuario:

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

La interfaz [IPamMetier] ofrecida a la capa [ui] por la capa [metier] debe cumplir 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á rellenar el combo [1]
  • línea 10: el método que permitirá obtener la información de las posiciones 6 a 24. Esta información se ha 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 de las posiciones 6 a 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 de las posiciones 6 a 11 sobre el empleado cuyo salario se calcula y la información de las posiciones 16 a 19 sobre sus indemnizaciones. No hay que olvidar aquí que un objeto [Employe] encapsula un objeto [Indemnites] que representa las indemnizaciones del empleado.
  • línea 9: la información de las posiciones 12 a 15
  • línea 10: la información de las posiciones 20 a 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 elementos del salario tal y como se explican en las reglas de negocio descritas en el apartado 3.2.
  • línea 4: el salario base del empleado, en función del número de horas trabajadas
  • línea 5: las deducciones cotisations aplicadas a este salario base
  • líneas 6 y 7: las indemnizaciones que se añaden al salario base, en función del índice del empleado y del número de días trabajados
  • línea 8: el salario net 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 concreto con sus prestaciones 
        protected Employe GetEmploye(string ss) {
            return PamDao.GetEmploye(ss);
        }

        // los cotisations 
        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 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 (protected) [GetEmploye] que invoca el método del mismo nombre de la capa [dao] – declarado protected para que las clases derivadas puedan acceder a él sin que sea público.
  • líneas 22-24: método interno (protected) [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.º SS del empleado 
            // 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 hay que 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 nóminas 
                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 por defecto. 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 apartado 7.3.2), salvo por los siguientes detalles:

  • línea 20: el objeto de id «pamdao» tiene el tipo [Pam.Dao.Service.PamDaoNHibernate] y se encuentra en el ensamblado [pam-dao-nhibernate]. La capa [dao] es la que se ha estudiado anteriormente.
  • Líneas 21-23: el objeto de id «pammetier» tiene el tipo [Pam.Metier.Service.PamMetier] y se encuentra en el ensamblado [pam-metier-dao-nhibernate]. Hay que 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 apartado 6.2 da 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 nóminas 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 era visual: se comprobaba en pantalla que se obtenían los resultados esperados. Pasamos ahora 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 [pam-metier-dao-nhibernate.dll].
  • en [5], el archivo [NUnit.cs] se incluirá en el ensamblado [pam-metier-dao-nhibernate.dll], pero no [Main.cs] ni [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 va a 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 métodos, concretamente a los 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 DLL [pam-metier.dll] en la carpeta [bin/Release].

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

  • los 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:

En el ejemplo anterior, las pruebas se han superado.

Trabajo práctico:


  • ejecutar en el equipo 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 de la DLL de la capa [metier]

Una vez escrita y probada la clase [PamMetier], se generará la DLL [pam-metier-dao-nhibernate.dll] de la capa [metier] siguiendo el método descrito en el apartado 7.3.5 Se tendrá cuidado de no incluir en 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 instanciada al inicio de 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] construidas 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] que configura la capa [metier] estudiada 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 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. Deben activarse mediante programación
    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 apartado 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 correctamente
        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. Los campos estáticos de las líneas 11-14 se comparten así entre todos los usuarios.
  • el método [Application_Start] se ejecuta una sola vez tras la instanciación de la clase. Es el método en el que se realiza, por lo general, la inicialización de la aplicación.

Los datos compartidos por todos los usuarios son los siguientes:

  • línea 11: la matriz 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 ha finalizado la inicialización (correctamente o con error)
  • línea 14: un valor booleano que indica si la inicialización ha finalizado 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 version 2.


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


Trabajo práctico:


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