Skip to content

7. A aplicação [SimuPaie] – versão 3 – arquitetura de 3 camadas com NHibernate


Leitura recomendada: «C# 2008, Capítulo 4: arquiteturas de 3 camadas, testes NUnit, framework Spring».


7.1. Arquitetura geral da aplicação

A aplicação [SimuPaie] terá agora a seguinte estrutura de três camadas:

  • A camada [1-dao] (dao = Data Access Object) irá gerir o acesso aos dados.
  • A camada [2-business] irá gerir a lógica de negócio da aplicação, especificamente os cálculos da folha de pagamentos.
  • A camada [3-ui] (ui = Interface do Utilizador) irá tratar da apresentação dos dados ao utilizador e da execução dos pedidos do utilizador. Referimo-nos ao conjunto de módulos que desempenham esta função como a [Aplicação]. Esta funciona como a interface do utilizador.
  • As três camadas serão tornadas independentes através da utilização de interfaces .NET
  • A integração das diferentes camadas será gerida pelo Spring IoC

O processamento de uma solicitação do cliente segue estas etapas:

  1. O cliente faz uma solicitação à aplicação.
  2. A aplicação processa este pedido. Para tal, poderá necessitar da assistência da camada [de negócios], que, por sua vez, poderá necessitar da camada [DAO] caso seja necessário trocar dados com a base de dados.
  3. A aplicação recebe uma resposta da camada [business]. Com base nessa resposta, envia a vista apropriada (= a resposta) ao cliente.

Tomemos o exemplo do cálculo do salário de uma ama. Isto exigirá vários passos:

  1. A camada [UI] terá de perguntar ao utilizador
    • a identidade da pessoa cujo salário deve ser calculado
    • o número de dias trabalhados por essa pessoa
    • o número de horas trabalhadas
  1. Para tal, terá de apresentar ao utilizador uma lista de pessoas (apelido, nome próprio, SSN) da tabela [EMPLOYEES], para que o utilizador possa selecionar uma delas. A camada [ui] utilizará o caminho [2, 3, 4, 5, 6, 7] para recuperar esta informação. A operação [2] é o pedido da lista de funcionários; a operação [7] é a resposta a esse pedido. Uma vez feito isto, a camada [ui] pode apresentar a lista de funcionários ao utilizador através de [8].
  2. O utilizador enviará à camada [ui] o número de dias trabalhados e o número de horas trabalhadas. Esta é a operação [1] acima. Durante esta etapa, o utilizador interage apenas com a camada [ui]. É esta camada que verificará a validade dos dados introduzidos. Uma vez feito isto, o utilizador solicitará o cálculo da folha de pagamentos.
  3. A camada [ui] solicitará à camada de negócios que realize este cálculo. Para tal, enviará os dados que recebeu do utilizador para a camada de negócios. Esta é a operação [2].
  4. A camada [business] necessita de determinadas informações para realizar a sua tarefa:
    • informações mais completas sobre a pessoa (morada, índice, etc.)
    • subsídios associados à sua categoria salarial
    • as taxas das várias contribuições para a segurança social a deduzir do salário bruto

Solicitará estas informações à camada [DAO] utilizando o caminho [3, 4, 5, 6]. [3] é o pedido inicial e [6] é a resposta a esse pedido.

  1. Tendo todos os dados necessários, a camada [business] calcula a remuneração da pessoa selecionada pelo utilizador.
  2. A camada [business] pode agora responder ao pedido da camada [ui] feito em (d). Este é o caminho [7].
  3. A camada [ui] irá formatar estes resultados para os apresentar ao utilizador de forma adequada e, em seguida, exibi-los. Este é o caminho [8].
  4. É possível imaginar que estes resultados precisam de ser armazenados num ficheiro ou numa base de dados. Isto pode ser feito automaticamente. Neste caso, após a operação (f), a camada [business] solicitará à camada [DAO] que guarde os resultados. Este será o caminho [3, 4, 5, 6]. Isto também pode ser feito a pedido do utilizador. O caminho [1-8] será utilizado pelo ciclo de pedido-resposta.

Como se pode ver nesta descrição, uma camada utiliza os recursos da camada à sua direita, nunca os da camada à sua esquerda.

A nossa primeira implementação desta arquitetura de três camadas será uma aplicação ASP.NET na qual

  • as camadas [DAO] e [business] serão implementadas por DLLs
  • e a camada [ui] será implementada pelo formulário web da versão 1 (ver secção 4.2.1).

Começamos por implementar a camada [DAO] utilizando o framework NHibernate.

7.2. A camada de acesso a dados [DAO]

7.2.1. O projeto C# do Visual Studio para a camada [DAO]

O projeto do Visual Studio para a camada [DAO] é o seguinte:

  • em [1], o projeto como um todo
  • em [2], as várias classes do projeto
  • em [3], as referências do projeto.
  • em [4], uma pasta [lib] contendo as DLLs necessárias para os vários projetos que se seguem

Nas referências do projeto [3], encontram-se as seguintes DLLs:

  • NHibernate: para o ORM NHibernate
  • MySql.Data: o controlador ADO.NET para o SGBD MySQL
  • Spring.Core: para o framework Spring
  • log4net: uma biblioteca de registo
  • nunit.framework: uma biblioteca de testes unitários

Estas referências foram retiradas da pasta [lib] [4]. Certifique-se de que a propriedade «Cópia local» para todas estas referências está definida como «True» [5]:

7.2.2. Entidades na camada [dao]

   

As entidades (objetos) necessárias para a camada [dao] foram reunidas na pasta [entities] do projeto. Algumas já nos são familiares: [Contributions], descrita na secção 6.3.2.1, [Employee], descrita na secção 6.3.2.3, e [Allowances], descrita na secção 6.3.2.2. Todas elas se encontram no namespace [Pam.Dao.Entities].

A classe [Employee] é definida da seguinte forma:


namespace Pam.Dao.Entites {
    public class Employe {
        // automatic properties
        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; }
 
        // manufacturers
        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. A classe [PamException]

A camada [dao] é responsável pela troca de dados com uma fonte externa. Esta troca pode falhar. Por exemplo, se forem solicitadas informações a um serviço remoto na Internet, a sua recuperação falhará devido a qualquer falha na rede. Para este tipo de erro, é prática comum em Java lançar uma exceção. Se a exceção não for do tipo [RunTimeException] ou de um tipo derivado, deve indicar na assinatura do método que o método lança uma exceção. No .NET, todas as exceções são não tratadas, ou seja, equivalentes ao tipo [RunTimeException] do Java. Portanto, não há necessidade de declarar que os métodos [GetAllEmployeeIDs, GetEmployee, GetContributions] são suscetíveis de lançar uma exceção.

No entanto, é útil poder distinguir entre diferentes exceções, uma vez que o seu tratamento pode variar. Assim, o código que trata vários tipos de exceções pode ser escrito da seguinte forma:

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{
...
}

Criamos, portanto, um tipo de exceção para a camada [DAO] da nossa aplicação. Trata-se do seguinte tipo [PamException]:


using System;
namespace Pam.Dao.Entites {
 
    public class PamException : Exception {
 
        // the error code 
        public int Code { get; set; }
 
        // manufacturers 
        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;
        }
    }
}
  • linha 2: a classe pertence ao namespace [Pam.Dao.Entities]
  • linha 4: a classe deriva da classe [Exception]
  • linha 7: possui uma propriedade pública [Code] que é um código de erro
  • Na nossa camada [dao], utilizaremos dois tipos de construtores:
    • o das linhas 18–21, que pode ser utilizado como mostrado abaixo:
throw new PamException("Problème d'accès aux données",5);
  • (continuação)
    • ou o das linhas 23–26, concebido para propagar uma exceção que já ocorreu, envolvendo-a numa exceção [PamException]:
try{
....
}catch (IOException ex){
    // on encapsule l'exception
    throw new PamException("Problème d'accès aux données",ex,10);
}

Este segundo método tem a vantagem de não perder a informação contida na primeira exceção.

7.2.4. Ficheiros de mapeamento do NHibernate <--> classes

Voltemos à arquitetura da aplicação:

Durante a leitura, o framework NHibernate recupera dados da base de dados e transforma-os em objetos, cujas classes acabámos de apresentar. Durante a gravação, faz o oposto: a partir dos objetos, cria, atualiza e elimina linhas nas tabelas da base de dados. Os ficheiros responsáveis pela transformação tabela <--> classe já foram apresentados:

   
  • O ficheiro [Cotisations.hbm.xml] apresentado na secção 6.3.2.1 mapeia a tabela [COTISATIONS] para a classe [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>
  • O ficheiro [Employe.hbm.xml] apresentado na secção 6.3.2.3 mapeia a tabela [EMPLOYEES] para a classe [Employee]

<?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>
  • O ficheiro [Indemnites.hbm.xml] apresentado na secção 6.3.2.2 mapeia a tabela [INDEMNITES] para a classe [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>

Note que na tag <hibernate-mapping> destes ficheiros (linha 2), estão presentes os seguintes atributos:

  • namespace: Pam.Dao.Entities. As classes [Contributions], [Employee] e [Allowances] devem estar localizadas neste namespace.
  • assembly: pam-dao-nhibernate. Os ficheiros de mapeamento [*.hbm.xml] devem estar encapsulados numa DLL denominada [pam-dao-nhibernate]. Para tal, o projeto C# é configurado da seguinte forma:
  • em [1], o assembly do projeto é denominado [pam-dao-nhibernate]
  • em [2], os ficheiros de mapeamento [*.hbm.xml] são incluídos [3] na montagem do projeto

7.2.5. A interface [ IPamDao] da camada [DAO]

Voltemos à arquitetura da nossa aplicação:

Em casos simples, podemos começar pela camada [business] para descobrir as interfaces da aplicação. Para funcionar, ela precisa de dados:

  • já disponíveis em ficheiros, bases de dados ou através da rede. Estes são fornecidos pela camada [dao].
  • ainda não disponíveis. São então fornecidos pela camada [ui], que os obtém do utilizador da aplicação.

Que interface deve a camada [DAO] fornecer à camada [business]? Que interações são possíveis entre estas duas camadas? A camada [DAO] deve fornecer os seguintes dados à camada [business]:

  • a lista de prestadores de cuidados infantis para permitir que o utilizador selecione um específico
  • informações completas sobre a pessoa selecionada (morada, índice, etc.)
  • os subsídios associados ao índice da pessoa
  • as taxas das várias contribuições para a segurança social

Estas informações são conhecidas antes do cálculo da folha de pagamentos e podem, portanto, ser armazenadas. Na direção [negócio] -> [DAO], a camada [negócio] pode solicitar que a camada [DAO] registe o resultado do cálculo da folha de pagamentos. Não faremos isso aqui.

Com esta informação, poderíamos tentar uma definição inicial da interface da camada [dao]:


using Pam.Dao.Entites;
 
namespace Pam.Dao.Service {
    public interface IPamDao {
        // list of all employee identities 
        Employe[] GetAllIdentitesEmployes();
        // an individual employee with benefits 
        Employe GetEmploye(string ss);
        // list of all contributions 
        Cotisations GetCotisations();
    }
}
  • Linha 1: Importamos o namespace para entidades na camada [dao].
  • Linha 3: A camada [dao] encontra-se no namespace [Pam.Dao.Service]. Os elementos no namespace [Pam.Dao.Entities] podem ser criados em múltiplas instâncias. Os elementos no namespace [Pam.Dao.Service] são criados como uma única instância (singleton). Foi isto que justificou a escolha dos nomes dos namespaces.
  • Linha 4: A interface é denominada [IPamDao]. Define três métodos:
    • Linha 6: [GetAllIdentitesEmployes] devolve uma matriz de objetos do tipo [Employe] que representa a lista de prestadores de cuidados infantis num formato simplificado (apelido, nome próprio, SS).
    • Linha 8: [GetEmploye] devolve um objeto [Employe]: o funcionário com o número de segurança social passado como parâmetro ao método, juntamente com os benefícios associados ao seu nível salarial.
    • Linha 10: [GetCotisations] devolve o objeto [Cotisations], que encapsula as taxas das várias contribuições para a segurança social a deduzir do salário bruto.

7.3. Implementação e teste da camada [dao]

7.3.1. O projeto do Visual Studio

O projeto do Visual Studio já foi apresentado. A título de recordação:

  • em [1], o projeto como um todo
  • em [2], as várias classes do projeto. A pasta [entities] contém as entidades tratadas pela camada [dao], bem como os ficheiros de mapeamento do NHibernate. A pasta [service] contém a interface [IPamDao] e a sua implementação [PamDaoNHibernate]. A pasta [tests] contém um teste de consola [Main.cs] e um teste unitário [NUnit.cs].
  • em [3], as referências do projeto.

7.3.2. O programa de teste de consola [Main.cs]

O programa de teste [Main.cs] é executado na seguinte arquitetura:

É responsável por testar os métodos da interface [IPamDao]. Um exemplo básico pode ser o seguinte:


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 {
                // layer instantiation [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // list of employee identities 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // an employee with benefits 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // list of contributions 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // exception display 
                Console.WriteLine(ex.ToString());
            }
            //break 
            Console.ReadLine();
        }
    }
}
  • Linha 11: Solicitamos ao Spring uma referência à camada [dao].
  • linhas 13–15: teste do método [GetAllEmployeeIDs] da interface [IPamDao]
  • linha 18: teste do método [GetEmploye] da interface [IPamDao]
  • linha 21: teste do método [GetCotisations] da interface [IPamDao]

O Spring, o NHibernate e o log4net são configurados pelo seguinte ficheiro [ App.config]:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- configuration sections -->
    <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>
 
 
    <!-- spring configuration -->
    <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>
 
    <!-- configuration 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>
 
    <!-- This section contains the log4net configuration settings -->
    <!-- NOTE IMPORTANTE: logs are not active by default. They must be activated by program
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
    <log4net>
    ...
    </log4net>
 
</configuration>

A configuração do NHibernate (linha 10, linhas 25–36) foi explicada na Secção 6.3.1. Repare na linha 34, que especifica que os ficheiros de mapeamento se encontram no assembly [pam-dao-nhibernate]. Este é o assembly do projeto.

A configuração do Spring é definida nas linhas 6–9 e 15–22. A linha 20 define o objeto [pamdao] utilizado pelo programa de consola [Main.cs]. A tag <object> possui os seguintes atributos aqui:

  • type: Especifica a classe a instanciar. Esta é a classe [PamDaoNHibernate], que implementa a interface [IPamDao]. Pode ser encontrada na DLL [pam-dao-nhibernate] do projeto.
  • init-method: o método da classe [PamDaoNHibernate] a ser executado após a instância da classe
  • destroy-method: o método da classe [PamDaoNHibernate] a ser executado quando o contentor Spring for destruído no final da execução do projeto.

A execução utilizando a base de dados descrita na Secção 6.2 produz a seguinte saída na 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]
  • linhas 1-2: os 2 funcionários do tipo [Funcionário] com apenas as informações [SS, Apelido, Nome]
  • linha 4: o funcionário do tipo [Employee] com o número de segurança social [254104940426058]
  • linha 5: taxas de contribuição

7.3.3. Definição da classe [PamDaoNHibernate]

A interface [IPamDao] implementada pela camada [dao] é a seguinte:


using Pam.Dao.Entites;
 
namespace Pam.Dao.Service {
    public interface IPamDao {
        // list of all employee identities 
        Employe[] GetAllIdentitesEmployes();
        // an individual employee with benefits 
        Employe GetEmploye(string ss);
        // list of all contributions 
        Cotisations GetCotisations();
    }
}

Pergunta: Escreva o código para a classe [PamDaoNHibernate] que implementa a interface [IPamDao] acima, utilizando o framework NHibernate configurado conforme descrito anteriormente. Também iremos implementar os métodos init e destroy executados pelo Spring. O método init irá criar a SessionFactory a partir da qual obteremos objetos Session. O método destroy irá fechar esta SessionFactory. Iremos utilizar os exemplos da Secção 6.5.


Restrições:

Partiremos do princípio de que determinados dados solicitados à camada [dao] cabem inteiramente na memória. Assim, para melhorar o desempenho, a classe [PamDaoNHibernate] armazenará:

  • a tabela [EMPLOYEES] no formato (SS, LAST_NAME, FIRST_NAME) exigido pelo método [GetAllEmployeeIDs], como uma matriz de objetos [Employee]
  • a tabela [COTISATIONS] na forma de um único objeto do tipo [Cotisations]

Isto será feito no método [init] da classe. O esqueleto da classe [PamDaoNHibernate] poderia ser o seguinte:


using System;
...
 
namespace Pam.Dao.Service {
    class PamDaoNHibernate : IPamDao {
        // private fields 
        private Cotisations cotisations;
        private Employe[] employes;
        private ISessionFactory sessionFactory = null;
 
        // init 
        public void init() {
            try {
                // factory initialization
                sessionFactory = new Configuration().Configure().BuildSessionFactory();
                // retrieve contribution rates and employees for caching 
.......................
        }
 
        // closure SessionFactory
        public void destroy() {
            if (sessionFactory != null) {
                sessionFactory.Close();
            }
        }
 
        // list of all employee identities 
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }
 
        // an individual employee with benefits 
        public Employe GetEmploye(string ss) {
................................
        }
 
        // list of contributions 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}

7.3.4. Testes unitários com NUnit


Leitura recomendada: «C# 2008, Capítulo 4: Arquiteturas de três camadas, testes NUnit, framework Spring».


O teste anterior era visual: verificámos no ecrã para garantir que estávamos a obter os resultados esperados. Este método é insuficiente num ambiente profissional. Os testes devem ser sempre automatizados tanto quanto possível e ter como objetivo não requerer intervenção humana. Os seres humanos são, de facto, propensos à fadiga, e a sua capacidade de verificar testes diminui ao longo do dia. A ferramenta [NUnit] ajuda a alcançar esta automatização. Está disponível no URL [http://www.nunit.org/].

O projeto do Visual Studio para a camada [dao] evoluirá da seguinte forma:

  • em [1], o programa de teste [NUnit.cs]
  • em [2,3], o projeto irá gerar uma DLL denominada [pam-dao-hibernate.dll]
  • em [4], a referência à DLL do framework NUnit: [nunit.framework.dll]
  • em [5], a classe [Main.cs] não será incluída na DLL [pam-dao-hibernate]
  • Em [6], a classe [NUnit.cs] será incluída na DLL [pam-dao-hibernate]

A classe de teste NUnit <a id="pam-dao-nhibernate-nunit"></a> é a seguinte:


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 {
        // the [dao] layer to be tested 
        private IPamDao pamDao = null;
 
        // manufacturer 
        public NunitPamDao() {
            // layer instantiation [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }
 
        // init 
        [SetUp]
        public void Init() {
 
        }
 
        [Test]
        public void GetAllIdentitesEmployes() {
            // audit no. of employees 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }
 
        [Test]
        public void GetCotisations() {
            // checking contribution rates 
            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() {
            // individual verification 
            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() {
            // non-existent individual verification 
            bool erreur = false;
            try {
                Employe employe1 = pamDao.GetEmploye("xx");
            } catch {
                erreur = true;
            }
            Expect(erreur, True);
        }
    }
}
  • linha 11: a classe possui o atributo [TestFixture], o que a torna uma classe de teste [NUnit].
  • linha 12: a classe deriva da classe utilitária AssertionHelper da estrutura NUnit (a partir da versão 2.4.6).
  • linha 14: o campo privado [pamDao] é uma instância da interface que fornece acesso à camada [dao]. Note-se que o tipo deste campo é uma interface, não uma classe. Isto significa que a instância [pamDao] torna acessíveis apenas métodos — especificamente, os da interface [IPamDao].
  • Os métodos testados na classe são aqueles com o atributo [Test]. Para todos estes métodos, o processo de teste é o seguinte:
    • O método com o atributo [SetUp] é executado primeiro. É utilizado para preparar os recursos (ligações de rede, ligações à base de dados, etc.) necessários para o teste.
    • Em seguida, o método a ser testado é executado
    • e, finalmente, é executado o método com o atributo [TearDown]. Este é geralmente utilizado para libertar os recursos alocados pelo método com o atributo [SetUp].
  • No nosso teste, não há recursos para alocar antes de cada teste e depois desalocar. Portanto, não precisamos de métodos com os atributos [SetUp] e [TearDown]. Para o exemplo, incluímos, nas linhas 23–26, um método com o atributo [SetUp].
  • Linhas 17–20: O construtor da classe inicializa o campo privado [pamDao] utilizando o Spring e o [App.config].
  • Linhas 29–32: Testar o método [GetAllIdentitesEmployes]
  • Linhas 35–42: Teste o método [GetCotisations]
  • Linhas 45–53: Teste o método [GetEmploye]
  • Linhas 56–65: Teste o método [GetEmploye] quando ocorre uma exceção.

A compilação do projeto cria a DLL [pam-dao-nhibernate.dll] na pasta [bin/Release].

A pasta [bin/Release] também contém:

  • as DLLs que fazem parte das referências do projeto e têm o atributo [Cópia Local] definido como verdadeiro: [Spring.Core, MySql.data, NHibernate, log4net]. Estas DLLs são acompanhadas por cópias das DLLs que elas próprias utilizam:
    • [CastleDynamicProxy, Iesi.Collections] para a ferramenta NHibernate
    • [antlr.runtime, Common.Logging] para a ferramenta Spring
  • O ficheiro [pam-dao-nhibernate.dll.config] é uma cópia do ficheiro de configuração [App.config]. O VS realiza esta duplicação. Em tempo de execução, é utilizado o ficheiro [pam-dao-nhibernate.dll.config], e não o [App.config].

Carregamos a DLL [pam-dao-nhibernate.dll] utilizando a ferramenta [NUnit-Gui], versão 2.4.6, e executamos os testes:

Image

Acima, os testes foram bem-sucedidos.

Exercício prático:


  • Implemente os testes para a classe [PamDaoNHibernate] numa máquina.
  • Utilize diferentes ficheiros de configuração [App.config] para utilizar diferentes SGBDs (Firebird, MySQL, Postgres, SQL Server)

7.3.5. Gerar a DLL da camada [ ] e da camada [dao]

Depois de a classe [PamDaoNHibernate] ter sido escrita e testada, a DLL da camada [dao] será gerada da seguinte forma:

  • [1], os programas de teste são excluídos da compilação do projeto
  • [2,3], configuração do projeto
  • [4], compilação do projeto
  • A DLL é gerada na pasta [bin/Release] [5]. Adicionamo-la às DLLs já presentes na pasta [lib] [6]:

7.4. A camada de negócios

Vamos rever a arquitetura geral da aplicação [SimuPaie]:

Partimos do princípio de que a camada [dao] está concluída e foi encapsulada na DLL [pam-dao-nhibernate.dll]. Vamos agora concentrar-nos na camada [business]. Esta camada implementa as regras de negócio, neste caso as regras para o cálculo de um salário.

7.4.1. O projeto do Visual Studio [ ] para a camada [business]

O projeto do Visual Studio para a camada de negócios pode ter o seguinte aspeto:

  • Em [1], todo o projeto é configurado pelo ficheiro [App.config]
  • em [2], a camada [business] é composta por duas pastas [entities, service]. A pasta [tests] contém um programa de teste de consola (Main.cs) e um programa de teste NUnit (NUnit.cs).
  • em [3] as referências utilizadas pelo projeto. Repare na DLL [pam-dao-hibernate] da camada [DAO] discutida anteriormente.

7.4.2. A interface [IPamM tier] da camada [business]

Voltemos à arquitetura geral da aplicação:

Que interface deve a camada [de negócios] fornecer à camada [UI]? Quais são as possíveis interações entre estas duas camadas? Recordemos a interface web que será apresentada ao utilizador:

  1. Quando o formulário for exibido pela primeira vez, a lista de funcionários deve aparecer em [1]. Uma lista simplificada é suficiente (Apelido, Nome, Número de Segurança Social). O Número de Segurança Social é necessário para aceder a informações adicionais sobre o funcionário selecionado (informações 6 a 11).
  2. As informações 12 a 15 correspondem às várias taxas de contribuição.
  3. As informações 16 a 19 são os subsídios associados ao índice do funcionário
  4. As informações 20 a 24 consistem em componentes salariais calculados com base nas entradas do utilizador 1 a 3.

A interface [IPamMetier] fornecida à camada [ui] pela camada [metier] deve cumprir os requisitos acima. Existem muitas interfaces possíveis. Propomos o seguinte:


using Pam.Dao.Entites;
using Pam.Metier.Entites;
 
namespace Pam.Metier.Service {
    public interface IPamMetier {
        // list of all employee identities 
        Employe[] GetAllIdentitesEmployes();

        // ------- salary calculation 
        FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
    }
}
  • linha 7: o método que irá preencher a caixa de combinação [1]
  • linha 10: o método que irá recuperar as informações 6 a 24. Estas informações foram recolhidas num objeto do tipo [PayrollSheet].

7.4.3. Entidades na camada [business]

A pasta [entities] no projeto do Visual Studio contém os objetos tratados pela classe de negócios: [Payroll] e [PayrollItems].

A classe [FeuilleS al] encapsula os campos 6 a 24 do formulário anterior:


using Pam.Dao.Entites;
 
namespace Pam.Metier.Entites {
 
    public class FeuilleSalaire {
 
        // automatic properties 
        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);
        }
    }
}
  • Linha 8: Informações 6 a 11 sobre o funcionário cujo salário está a ser calculado e informações 16 a 19 sobre os seus subsídios. Note-se que um objeto [Employee] encapsula um objeto [Allowances] que representa os subsídios do funcionário.
  • linha 9: informações 12 a 15
  • linha 10: informações 20 a 24
  • linhas 13–15: o método [ToString]

A classe [Elements Salaire] encapsula as informações 20 a 24 do formulário:


namespace Pam.Metier.Entites {
    public class ElementsSalaire {
        // automatic properties 
        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);
        }
    }
}
  • linhas 4–8: os componentes salariais, conforme explicado nas regras de negócio descritas na secção 3.2.
  • linha 4: o salário base do funcionário, com base no número de horas trabalhadas
  • linha 5: contribuições deduzidas deste salário base
  • linhas 6 e 7: subsídios a acrescentar ao salário base, com base no grau do funcionário e no número de dias trabalhados
  • linha 8: o salário líquido a pagar
  • Linhas 12–15: O método [ToString] da classe.

7.4.4. Implementação da camada [business]

Iremos implementar a interface [IPamMetier] com duas classes:

  • [AbstractBasePamMetier], que é uma classe abstrata na qual implementaremos o acesso aos dados para a interface [IPamMetier]. Esta classe terá uma referência à camada [dao].
  • [PamMetier], uma classe derivada de [AbstractBasePamMetier], que irá implementar as regras de negócio da interface [IPamMetier]. Não terá conhecimento da camada [dao].

A classe [AbstractBasePamMetier] será a seguinte:


using Pam.Dao.Entites;
using Pam.Dao.Service;
using Pam.Metier.Entites;
 
namespace Pam.Metier.Service {
    public abstract class AbstractBasePamMetier : IPamMetier {
 
        // data access object 
        public IPamDao PamDao { get; set; }
 
        // list of all employee identities 
        public Employe[] GetAllIdentitesEmployes() {
            return PamDao.GetAllIdentitesEmployes();
        }
 
        // an individual employee with benefits 
        protected Employe GetEmploye(string ss) {
            return PamDao.GetEmploye(ss);
        }
 
        // contributions 
        protected Cotisations GetCotisations() {
            return PamDao.GetCotisations();
        }
 
        // salary calculation 
        public abstract FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
    }
}
  • Linha 5: A classe pertence ao namespace [Pam.Metier.Service], tal como todas as classes e interfaces na camada [business].
  • linha 6: a classe é abstrata (atributo abstract) e implementa a interface [IPamMetier]
  • linha 9: a classe contém uma referência à camada [dao] na forma de uma propriedade pública
  • linhas 12–14: implementação do método [GetAllEmployeIDs] da interface [IPamMetier] – utiliza o método com o mesmo nome na camada [dao]
  • linhas 17–19: método interno (protegido) [GetEmployee] que chama o método com o mesmo nome na camada [dao] – declarado como protegido para que as classes derivadas possam aceder-lhe sem que seja público
  • linhas 22-24: método interno (protegido) [GetContributions] que chama o método com o mesmo nome na camada [dao]
  • linha 27: implementação abstrata (atributo abstract) do método [GetSalary] da interface [IPamMetier].

O cálculo do salário é implementado pela seguinte classe [PamMetier]:


using System;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
 
namespace Pam.Metier.Service {
 
    public class PamMetier : AbstractBasePamMetier {
 
        // wage calculation 
        public override FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés) {
            // SS : employee's SS number 
            // HeuresTravaillées: number of hours worked 
            // Days worked: number of days worked 
            // we get the employee back with his benefits 
            ...
            // we recover the various contribution rates 
            ...
            // salary components are calculated 
            ...
            // we return the payslip 
            return ...;
        }
    }
}
  • linha 7: a classe deriva de [AbstractBasePamMetier] e, portanto, implementa a interface [IPamMetier]
  • linha 10: o método [GetSalary] a ser implementado

Pergunta: Escreva o código para o método [GetSalaire].


7.4.5. O teste de consola para a camada [business]

Vamos rever o projeto do Visual Studio para a camada [business]:

O programa de teste [Main] acima testa os métodos da interface [IPamMetier]. Um exemplo básico pode ser semelhante a este:


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 {
                // instantiation layer [metier]
                IPamMetier pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
                // payslip calculations 
                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()));
            }
            // break 
            Console.ReadLine();
        }
    }
}
  • Linha 11: Instanciação Spring da camada [business].
  • Linhas 13–14: teste do método [GetSalary] da interface [IPamMetier]
  • Linhas 15–22: Teste do método [GetSalaire] quando ocorre uma exceção

O programa de teste utiliza o seguinte ficheiro de configuração [ App.config]:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- configuration sections -->
    <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>
 
 
    <!-- spring configuration -->
    <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>
 
    <!-- configuration NHibernate -->
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
....
    </hibernate-configuration>
 
    <!-- This section contains the log4net configuration settings -->
    <!-- NOTE IMPORTANTE: logs are not active by default. They must be activated by program
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
    <log4net>
...
    </log4net>
 
</configuration>

Este ficheiro é idêntico ao ficheiro [App.config] utilizado no projeto da camada [dao] (ver secção 7.3.2), com as seguintes pequenas diferenças:

  • linha 20: o objeto com ID "pamdao" é do tipo [Pam.Dao.Service.PamDaoNHibernate] e encontra-se no assembly [pam-dao-nhibernate]. A camada [dao] é a que foi discutida anteriormente.
  • linhas 21–23: o objeto com ID "pammetier" é do tipo [Pam.Metier.Service.PamMetier] e está localizado no assembly [pam-metier-dao-nhibernate]. O projeto deve ser configurado da seguinte forma:
 
  • Linha 22: O objeto [PamMetier] instanciado pelo Spring possui uma propriedade pública [PamDao] que é uma referência à camada [dao]. Esta propriedade é inicializada com a referência à camada [dao] criada na linha 20.

A execução utilizando a base de dados descrita na secção 6.2 produz a seguinte saída na 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
  • Linhas 1-2: Os 2 recibos de vencimento solicitados
  • linha 3: a exceção [PamException] causada por um funcionário inexistente.

7.4.6. Testes unitários para a camada de negócios

O teste anterior foi visual: verificámos no ecrã que estávamos, de facto, a obter os resultados esperados. Vamos agora passar para os testes NUnit não visuais.

Voltemos ao projeto do Visual Studio para o projeto [business]:

  • em [1], o programa de teste NUnit
  • em [2], a referência à DLL [nunit.framework]
  • em [3,4], a compilação do projeto irá gerar a DLL [pam-metier-dao-nhibernate.dll].
  • em [5], o ficheiro [NUnit.cs] será incluído no assembly [pam-metier-dao-nhibernate.dll], mas não o [Main.cs] [6]

A classe de teste NUnit é a seguinte:


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 {
 
        // the [metier] layer to test 
        private IPamMetier pamMetier;
 
        // manufacturer
        public NunitTestPamMetier() {
            // layer instantiation [dao]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        }
 
 
        [Test]
        public void GetAllIdentitesEmployes() {
            // audit no. of employees 
            Expect(2, EqualTo(pamMetier.GetAllIdentitesEmployes().Length));
        }
 
        [Test]
        public void GetSalaire1() {
            // wage sheet calculation 
            FeuilleSalaire feuilleSalaire = pamMetier.GetSalaire("254104940426058", 150, 20);
            // checks 
            Expect(368.77, EqualTo(feuilleSalaire.ElementsSalaire.SalaireNet).Within(1E-06));
            // non-existent employee payslip 
            bool erreur = false;
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (PamException) {
                erreur = true;
            }
            Expect(erreur, True);
        }
 
    }
}
  • linha 13: o campo privado [pamMetier] é uma instância da interface que fornece acesso à camada [metier]. Note-se que o tipo deste campo é uma interface, não uma classe. Isto significa que a instância [PamMetier] torna acessíveis apenas os métodos da interface [IPamMetier].
  • linhas 16–19: O construtor da classe inicializa o campo privado [pamMetier] utilizando o Spring e o ficheiro de configuração [App.config].
  • Linhas 23–26: Teste o método [GetAllIdentitesEmployes]
  • Linhas 29–42: Teste o método [GetSalaire]

O projeto acima gera a DLL [pam-metier.dll] na pasta [bin/Release].

A pasta [bin/Release] também contém:

  • as DLLs que fazem parte das referências do projeto e têm o atributo [Cópia Local] definido como verdadeiro: [Spring.Core, MySql.data, NHibernate, log4net, pam-dao-nhibernate]. Estas DLLs são acompanhadas por cópias das DLLs que elas próprias utilizam:
    • [CastleDynamicProxy, Iesi.Collections] para a ferramenta NHibernate
    • [antlr.runtime, Common.Logging] para a ferramenta Spring
  • O ficheiro [pam-metier-dao-nhibernate.dll.config] é uma cópia do ficheiro de configuração [App.config].

Carregamos a DLL [pam-metier-dao-nhibernate.dll] com a ferramenta [NUnit-Gui, versão 2.4.6] e executamos os testes:

Image

Acima, os testes foram bem-sucedidos.

Exercício prático:


  • Implemente os testes para a classe [PamMetier] na máquina.
  • Utilize diferentes ficheiros de configuração App.config para utilizar diferentes SGBDs (Firebird, MySQL, Postgres, SQL Server)

7.4.7. Gerar a DLL da camada [business]

Depois de a classe [PamMetier] ter sido escrita e testada, iremos gerar a DLL [pam-metier-dao-hibernate.dll] para a camada [business], seguindo o método descrito na secção 7.3.5. Teremos o cuidado de não incluir os programas de teste [Main.cs] e [NUnit.cs] na DLL. Em seguida, colocá-la-emos na pasta [lib] das DLLs [1].

7.5. A camada [web]

Vamos rever a arquitetura geral da aplicação [SimuPaie]:

Partimos do princípio de que as camadas [dao] e [business] estão concluídas e encapsuladas nas DLLs [pam-dao-hibernate, pam-business-dao-hibernate.dll]. Vamos agora descrever a camada web.

7.5.1. O projeto do Visual Web Developer para a camada [web]

  • em [1], o projeto como um todo:
    • [Global.asax]: a classe instanciada quando a aplicação web é iniciada, que lida com a inicialização da aplicação
    • [Default.aspx]: a página de formulário web
  • em [2], as DLLs necessárias para a aplicação web. Repare nas DLLs para as camadas [DAO] e [business] criadas anteriormente.

7.5.2. Configuração da aplicação

O ficheiro [Web.config] que configura a aplicação define os mesmos dados que o ficheiro [App.config] que configura a camada [business] discutida anteriormente. Estes dados devem ser colocados no código pré-gerado do ficheiro [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>
 
  <!-- spring configuration -->
  <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>
 
  <!-- configuration 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>
 
  <!-- This section contains the log4net configuration settings -->
  <!-- NOTE IMPORTANTE: logs are not active by default. They must be activated by program
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
  <log4net>
....
  </log4net>
 
  <appSettings/>
  <connectionStrings/>

  <system.web>
....
....
 
</configuration>

As linhas 9–12, 18–28 e 31–44 contêm a configuração do Spring e do NHibernate descrita no ficheiro [App.config] da camada [business] (ver Secção 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
  {
    // --- static application data ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;
    public static string Msg;
    public static bool Erreur = false;
 
    // application startup
    public void Application_Start(object sender, EventArgs e)
    {
      // using the configuration file
      try
      {
        // instantiation layer [metier]
        PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        // simplified list of employees
        Employes = PamMetier.GetAllIdentitesEmployes();
        // we succeeded
        Msg = "Base chargée...";
      }
      catch (Exception ex)
      {
        // we note the error
        Msg = string.Format("L'erreur suivante s'est produite lors de l'accès à la base de données : {0}", ex);
        Erreur = true;
      }
    }
  }
}

Note que:

  • a classe [Global.asax.cs] é instanciada quando a aplicação é iniciada, e esta instância é acessível a todos os pedidos de todos os utilizadores. Os campos estáticos nas linhas 11–14 são, portanto, partilhados entre todos os utilizadores.
  • O método [Application_Start] é executado apenas uma vez após a instância da classe. Este é o método onde a aplicação é normalmente inicializada.

Os dados partilhados por todos os utilizadores são os seguintes:

  • linha 11: a matriz de objetos [Employee] que armazenará a lista simplificada (SS, LAST_NAME, FIRST_NAME) de todos os funcionários
  • linha 12: uma referência à camada [business] encapsulada na DLL [pam-metier-dao-nhibernate.dll]
  • linha 13: uma mensagem indicando se a inicialização foi concluída com sucesso ou com um erro
  • linha 14: um valor booleano indicando se a inicialização foi concluída com um erro ou não.

Em [Application_Start]:

  • linha 23: o Spring instancia as camadas [business] e [DAO] e devolve uma referência à camada [business]. Esta é armazenada no campo estático [PamMetier] da linha 12.
  • linha 25: o array de funcionários é solicitado à camada [business]
  • linha 27: a mensagem de sucesso
  • linha 32: a mensagem de erro

7.5.3. O formulário [Default.a spx]

O formulário é o da versão 2.

Image


Pergunta: Utilizando o código C# da página [Default.aspx.cs] da versão 2 como guia, escreva o código [Default.aspx.cs] para a versão 3. A única diferença está no cálculo do salário. Enquanto a versão 2 utilizava a API ADO.NET para recuperar informações da base de dados, aqui utilizaremos o método GetSalaire do [business].


Exercício prático:


  • Implemente a aplicação web anterior numa máquina
  • utilize diferentes ficheiros de configuração [Web.config] para utilizar diferentes SGBDs (Firebird, MySQL, Postgres, SQL Server)