Skip to content

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


Leituras recomendadas: “Linguagem C# 2008, Capítulo 4: Arquiteturas de três camadas, testes NUnit, framework Spring”.


7.1. Arquitetura geral do aplicativo

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

  • a camada [1-dao] (DAO = Data Access Object) será responsável pelo acesso aos dados.
  • A camada [2-métier] será responsável pelo aspecto de negócios da aplicação, ou seja, o cálculo da folha de pagamento.
  • A camada [3-ui] (ui = User Interface) será responsável pela apresentação dos dados ao usuário e pela execução de suas solicitações. Denominamos [Application] o conjunto de módulos que assegura essa função. Ela é o ponto de contato com o usuário.
  • As três camadas serão tornadas independentes graças ao uso de interfaces .NET
  • A integração das diferentes camadas será realizada pelo Spring IoC

O processamento de uma solicitação de um cliente ocorre de acordo com as seguintes etapas:

  1. o cliente faz uma solicitação à aplicação.
  2. A aplicação processa essa solicitação. Para isso, ela pode precisar da ajuda da camada [métier], que, por sua vez, pode precisar da camada [dao] caso seja necessário trocar dados com o banco de dados.
  3. a aplicação recebe uma resposta da camada [métier]. Com base nessa resposta, ela envia a visualização (= a resposta) apropriada ao cliente.

Tomemos como exemplo o cálculo da folha de pagamento de uma babá. Esse cálculo exigirá várias etapas:

  1. a camada [ui] precisará solicitar ao usuário
    • a identidade da pessoa cujo salário se deseja calcular
    • o número de dias trabalhados por ela
    • o número de horas trabalhadas
  1. Para isso, ela deverá apresentar ao usuário a lista de pessoas (sobrenome, nome, SS) presentes na tabela [EMPLOYES], para que o usuário escolha uma delas. A camada [ui] utilizará o caminho [2, 3, 4, 5, 6, 7] para obtê-las. A operação [2] é a solicitação da lista de funcionários, e a operação [7] é a resposta a essa solicitação. Feito isso, a camada [ui] pode apresentar a lista de funcionários ao usuário por meio da [8].
  2. O usuário transmitirá à camada [ui] o número de dias trabalhados, bem como o número de horas trabalhadas. Essa é a operação [1] mencionada acima. Nesta etapa, o usuário interage apenas com a camada [ui]. É essa camada que verificará, entre outras coisas, a validade dos dados inseridos. Feito isso, o usuário solicitará o cálculo da folha de pagamento.
  3. A camada [ui] solicitará à camada de negócios que realize esse cálculo. Para isso, ela transmitirá a ela os dados que recebeu do usuário. Trata-se da operação [2].
  4. A camada [metier] precisa de certas informações para realizar seu trabalho:
    • informações mais completas sobre a pessoa (endereço, índice, etc.)
    • os benefícios associados ao seu índice
    • as alíquotas das diferentes contribuições sociais a serem deduzidas do salário bruto

Ela solicitará essas informações à camada [dao] por meio do caminho [3, 4, 5, 6]. [3] é a solicitação inicial e [6] é a resposta a essa solicitação.

  1. Com todos os dados de que precisava, a camada [metier] calcula a folha de pagamento da pessoa selecionada pelo usuário.
  2. A camada [metier] agora pode responder à solicitação da camada [ui] feita em (d). Esse é o caminho [7].
  3. A camada [ui] formatará esses resultados para apresentá-los ao usuário de maneira adequada e, em seguida, os exibirá. Esse é o caminho [8].
  4. É possível imaginar que esses resultados devam ser armazenados em um arquivo ou em um banco de dados. Isso pode ser feito automaticamente. Nesse caso, após a operação (f), a camada [metier] solicitará à camada [dao] que registre os resultados. Esse será o caminho [3, 4, 5, 6]. Isso também pode ser feito a pedido do usuário. Esse será o caminho [1-8], que será utilizado pelo ciclo solicitação-resposta.

Vemos nessa descrição que uma camada utiliza os recursos da camada à sua direita, nunca da que está à sua esquerda.

Nossa primeira implementação dessa arquitetura de três camadas será um aplicativo ASP.NET, no qual

  • as camadas [dao] e [metier] serão implementadas por DLL
  • a camada [ui] será implementada pelo formulário web da versão 1 (ver parágrafo 4.2.1).

Começamos implementando a camada [dao] com o framework NHibernate.

7.2. A camada [dao] de acesso aos dados

7.2.1. O projeto do Visual Studio C# da camada [dao]

O projeto do Visual Studio da camada [dao] é o seguinte:

  • em [1], o projeto como um todo
  • em [2], as diferentes classes do projeto
  • em [3], as referências do projeto.
  • em [4], uma pasta [lib] na qual foram reunidos os DLL necessários para os diversos projetos que se seguirão

Nas referências [3] do projeto, encontram-se os seguintes DLL:

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

Essas referências foram extraídas da pasta [lib] [4]. É preciso garantir que todas essas referências tenham a propriedade “Cópia local” definida como “True” [5]:

7.2.2. As entidades da camada [dao]

   

As entidades (objetos) necessárias para a camada [dao] foram reunidas na pasta [entites] do projeto. Algumas delas já nos são conhecidas: [Cotisations], descrita no parágrafo 6.3.2.1; [Employe], descrita no parágrafo 6.3.2.3; [Indemnites], descrita no parágrafo 6.3.2.2. Todas elas estão no espaço de nomes [Pam.Dao.Entites].

A classe [Employe] evolui da seguinte forma:


namespace Pam.Dao.Entites {
    public class Employe {
        // propriedades 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; }

        // construtores
        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 por trocar dados com uma fonte externa. Essa troca pode falhar. Por exemplo, se as informações forem solicitadas a um serviço remoto na Internet, sua obtenção falhará em caso de qualquer falha na rede. Nesse tipo de erro, é comum em Java lançar uma exceção. Se a exceção não for do tipo [RunTimeException] ou derivada, é necessário indicar na assinatura do método que este lança (throws) uma exceção. Em .NET, todas as exceções são não controladas, c.a.d. equivalentes ao tipo [RunTimeException] do Java. Portanto, não há necessidade de declarar que os métodos [GetAllIdentitesEmployes, GetEmploye, GetCotisations] podem lançar uma exceção.

No entanto, é interessante poder diferenciar as exceções umas das outras, pois seu tratamento pode variar. Assim, o código que lida com diversos tipos de exceções pode ser escrito da seguinte maneira:

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

Portanto, criamos um tipo de exceção para a camada [dao] do nosso aplicativo. Trata-se do seguinte tipo [PamException]:


using System;
namespace Pam.Dao.Entites {

    public class PamException : Exception {

        // código do erro 
        public int Code { get; set; }

        // construtores 
        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 espaço de nomes [Pam.Dao.Entites]
  • linha 4: a classe deriva da classe [Exception]
  • linha 7: ela possui uma propriedade pública [Code], que é um código de erro
  • utilizaremos em nossa camada [dao] dois tipos de construtor:
    • o das linhas 18 a 21, que pode ser usado conforme mostrado abaixo:
throw new PamException("Problème d'accès aux données",5);
  • (continuação)
    • ou o das linhas 23 a 26, destinado a reportar uma exceção já ocorrida, encapsulando-a em uma exceção do tipo [PamException]:
try{
....
}catch (IOException ex){
     // encapsulamos a exceção
    throw new PamException("Problème d'accès aux données",ex,10);
}

Esse segundo método tem a vantagem de não perder as informações que a primeira exceção possa conter.

7.2.4. Os arquivos de tabelas de mapeamento <--> classes do NHibernate

Voltemos à arquitetura do aplicativo:

Na leitura, o framework NHibernate utiliza dados do banco de dados e os transforma em objetos cujas classes acabamos de apresentar. Na gravação, ele faz o inverso: a partir de objetos, ele cria, atualiza e exclui linhas nas tabelas do banco de dados. Os arquivos responsáveis pela transformação tabelas <--> classes já foram apresentados:

   
  • o arquivo [Cotisations.hbm.xml], apresentado no parágrafo 6.3.2.1, estabelece a correspondência entre a tabela [COTISATIONS] e 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 arquivo [Employe.hbm.xml] apresentado no parágrafo 6.3.2.3 estabelece a correspondência entre a tabela [EMPLOYES] e a classe [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>
  • o arquivo [Indemnites.hbm.xml] apresentado no parágrafo 6.3.2.2 estabelece a correspondência entre a tabela [INDEMNITES] e 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>

Observe-se que, na tag <hibernate-mapping> desses arquivos (linha 2), temos os seguintes atributos:

  • namespace: Pam.Dao.Entites. As classes [Cotisations], [Employe] e [Indemnites] devem estar neste espaço de nomes.
  • assembly: pam-dao-nhibernate. Os arquivos de mapeamento [*.hbm.xml] devem ser encapsulados em um DLL denominado [pam-dao-nhibernate]. Para obter esse resultado, o projeto C# é configurado da seguinte forma:
  • em [1], o assembly do projeto tem o nome [pam-dao-nhibernate]
  • em [2], os arquivos de mapeamento [*.hbm.xml] são integrados ao assembly do projeto como [3]

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

Voltemos à arquitetura do nosso aplicativo:

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

  • já disponíveis em arquivos, bancos de dados ou pela rede. Esses dados são fornecidos pela camada [dao].
  • ainda não disponíveis. Nesse caso, são fornecidos pela camada [ui], que os obtém do usuário do aplicativo.

Que interface a camada [dao] deve oferecer à camada [metier]? Quais são as interações possíveis entre essas duas camadas? A camada [dao] deve fornecer os seguintes dados à camada [metier]:

  • a lista de babás, para permitir que o usuário escolha uma em particular
  • informações completas sobre a pessoa escolhida (endereço, índice, etc.)
  • os valores dos subsídios relacionados ao índice da pessoa
  • as alíquotas das diferentes contribuições sociais

Essas informações já são conhecidas antes do cálculo da folha de pagamento e, portanto, podem ser armazenadas. No sentido [metier] -> [dao], a camada [metier] pode solicitar à camada [dao] que registre o resultado do cálculo da folha de pagamento. Não faremos isso aqui.

Com essas informações, poderíamos tentar uma primeira definição da interface da camada [dao]:


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        // lista de todas as identidades dos funcionários 
        Employe[] GetAllIdentitesEmployes();
        // um funcionário específico com seus benefícios 
        Employe GetEmploye(string ss);
        // lista de todas as contribuições 
        Cotisations GetCotisations();
    }
}
  • linha 1: importamos o espaço de nomes das entidades da camada [dao].
  • linha 3: a camada [dao] está no espaço de nomes [Pam.Dao.Service]. Os elementos do espaço de nomes [Pam.Dao.Entites] podem ser criados em várias instâncias. Os elementos do espaço de nomes [Pam.Dao.Service] são criados em uma única instância (singleton). Foi isso que justificou a escolha dos nomes dos espaços de nomes.
  • linha 4: a interface se chama [IPamDao]. Ela define três métodos:
    • linha 6, [GetAllIdentitesEmployes] retorna um array de objetos do tipo [Employe] que representa a lista de babás em um formato simplificado (sobrenome, nome, SS).
    • linha 8, [GetEmploye] retorna um objeto [Employe]: o funcionário cujo número de previdência social foi passado como parâmetro ao método, juntamente com os valores de indenização associados ao seu índice.
    • linha 10, [GetCotisations] retorna o objeto [Cotisations], que encapsula as alíquotas das diferentes contribuições sociais a serem deduzidas do salário bruto.

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

7.3.1. O projeto do Visual Studio

O projeto do Visual Studio já foi apresentado. Vale lembrar:

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

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

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

Ele é responsável por testar os métodos da interface [IPamDao]. Um exemplo básico poderia 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 {
                // instanciação da camada [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // lista das identidades dos funcionários 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // um funcionário com suas indenizações 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // lista de contribuições 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // exibição de exceção 
                Console.WriteLine(ex.ToString());
            }
            //pausa 
            Console.ReadLine();
        }
    }
}
  • linha 11: solicita-se ao Spring uma referência à camada [dao].
  • linhas 13-15: teste do método [GetAllIdentitesEmployes] 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 arquivo [App.config] :


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- seções de configuração -->
    <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>


    <!-- configuração do 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>

    <!-- configuração 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 seção contém as configurações do log4net -->
    <!-- NOTE IMPORTANTE: os logs não estão ativos por padrão. É necessário ativá-los por meio do programa
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
    <log4net>
    ...
    </log4net>

</configuration>

A configuração do NHibernate (linha 10, linhas 25-36) foi explicada no parágrafo 6.3.1. Observe-se a linha 34, que indica que os arquivos de mapeamento estão localizados no assembly [pam-dao-nhibernate]. Esse é o assembly do projeto.

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

  • type: define a classe a ser instanciada. É a classe [PamDaoNHibernate] que implementa a interface [IPamDao]. Ela pode ser encontrada no arquivo DLL [pam-dao-nhibernate] do projeto.
  • init-method: o método da classe [PamDaoNHibernate] a ser executado após a instanciação da classe
  • destroy-method: o método da classe [PamDaoNHibernate] a ser executado quando o contêiner Spring for destruído ao final da execução do projeto.

A execução realizada com o banco de dados descrito no parágrafo 6.2 produz o seguinte resultado no console:

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 dois funcionários do tipo [Employe] com as seguintes informações [SS, Nom, Prenom]
  • linha 4: o funcionário do tipo [Employe] com o nº de previdência social [254104940426058]
  • linha 5: as alíquotas de contribuição

7.3.3. Lançamento da classe [PamDaoNHibernate]

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


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        // lista de todas as identidades dos funcionários 
        Employe[] GetAllIdentitesEmployes();
        // um funcionário específico com seus benefícios 
        Employe GetEmploye(string ss);
        // lista de todas as contribuições 
        Cotisations GetCotisations();
    }
}

Questão: escreva o código da classe [PamDaoNHibernate] que implementa a interface [IPamDao] acima, utilizando o framework NHibernate configurado conforme apresentado anteriormente. Também serão implementados os métodos init e destroy, executados pelo Spring. O método init criará a classe SessionFactory, da qual serão obtidos os objetos Session. O método destroy encerrará essa SessionFactory. Utilizaremos os exemplos do parágrafo 6.5.


Restrições:

Supõe-se que certos dados solicitados à camada [dao] possam caber inteiramente na memória. Assim, para melhorar o desempenho, a classe [PamDaoNHibernate] armazenará:

  • a tabela [EMPLOYES] na forma (SS, NOM, PRENOM) exigida pelo método [GetAllIdentitesEmployes] na forma de uma matriz de objetos do tipo [Employe]
  • a tabela [COTISATIONS] na forma de um único objeto do tipo [Cotisations]

Isso será feito no método [init] da classe. A estrutura básica da classe [PamDaoNHibernate] poderia ser a seguinte:


using System;
...

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

        // inicialização 
        public void init() {
            try {
                // inicialização da fábrica
                sessionFactory = new Configuration().Configure().BuildSessionFactory();
                // recuperamos as alíquotas de contribuição e os funcionários para armazená-los em cache 
.......................
        }

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

        // lista de todas as identidades dos funcionários 
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }

        // um funcionário específico com seus subsídios 
        public Employe GetEmploye(string ss) {
................................
        }

        // lista de contribuições 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}

7.3.4. Testes unitários com NUnit


Leituras recomendadas: “Linguagem C# 2008, Capítulo 4: Arquiteturas de três camadas, testes NUnit, framework Spring”.


O teste anterior foi visual: verificávamos na tela se estávamos obtendo os resultados esperados. Esse método é insuficiente no ambiente profissional. Os testes devem sempre ser automatizados ao máximo e ter como objetivo não exigir nenhuma intervenção humana. O ser humano está, de fato, sujeito à fadiga e sua capacidade de verificar testes diminui ao longo do dia. A ferramenta [NUnit] ajuda a realizar essa automação. Ela está disponível no URL [http://www.nunit.org/].

O projeto do Visual Studio da camada [dao] evoluirá da seguinte maneira:

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

A classe de teste NUnit é 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 {
        // a camada [dao] a ser testada 
        private IPamDao pamDao = null;

        // fabricante 
        public NunitPamDao() {
            // instanciação da camada [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }

        // inicialização 
        [SetUp]
        public void Init() {

        }

        [Test]
        public void GetAllIdentitesEmployes() {
            // verificação do número de funcionários 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetCotisations() {
            // verificação da alíquota de contribuições 
            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() {
            // verificação de pessoas físicas 
            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() {
            // verificação de indivíduo inexistente 
            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 do framework NUnit (a partir da versão 2.4.6).
  • linha 14: o campo privado [pamDao] é uma instância da interface de acesso à camada [dao]. Observe-se que o tipo desse campo é uma interface e não uma classe. Isso significa que a instância [pamDao] torna acessíveis apenas os métodos da interface [IPamDao].
  • Os métodos testados na classe são aqueles que possuem o atributo [Test]. Para todos esses métodos, o processo de teste é o seguinte:
    • primeiramente, é executado o método com o atributo [SetUp]. Ele serve para preparar os recursos (conexões de rede, conexões com bancos de dados, etc.) necessários para o teste.
    • Em seguida, o método a ser testado é executado
    • e, por fim, o método com o atributo [TearDown] é executado. Ele geralmente serve para liberar os recursos mobilizados pelo método com o atributo [SetUp].
  • Em nosso teste, não há recursos a serem alocados antes de cada teste e posteriormente desalocados. Portanto, não precisamos de métodos com os atributos [SetUp] e [TearDown]. Para o exemplo, apresentamos, nas linhas 23 a 26, um método com o atributo [SetUp].
  • linhas 17-20: o construtor da classe inicializa o campo privado [pamDao] usando o Spring e [App.config].
  • linhas 29-32: testam o método [GetAllIdentitesEmployes]
  • linhas 35-42: testam o método [GetCotisations]
  • linhas 45-53: testam o método [GetEmploye]
  • linhas 56-65: testam o método [GetEmploye] durante uma exceção.

A geração do projeto cria os arquivos DLL e [pam-dao-nhibernate.dll] na pasta [bin/Release].

A pasta [bin/Release] contém, além disso:

  • os arquivos DLL que fazem parte das referências do projeto e que têm o atributo [Copie locale] definido como verdadeiro: [Spring.Core, MySql.data, NHibernate, log4net]. Esses DLL vêm acompanhados de cópias dos DLL que eles próprios utilizam:
    • [CastleDynamicProxy, Iesi.Collections] para a ferramenta NHibernate
    • [antlr.runtime, Common.Logging] para a ferramenta Spring
  • o arquivo [pam-dao-nhibernate.dll.config] é uma cópia do arquivo de configuração [App.config]. É o VS que realiza essa duplicação. Na execução, é utilizado o arquivo [pam-dao-nhibernate.dll.config] e não o [App.config].

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

Image

Acima, os testes foram bem-sucedidos.

Trabalho prático:


  • executar na máquina os testes da classe [PamDaoNHibernate].
  • utilizar diferentes arquivos de configuração [App.config] para usar diferentes SGBD (Firebird, MySQL, Postgres, SQL Server)

7.3.5. Geração d e da DLL a partir da camada [dao]

Após a criação e o teste da classe [PamDaoNHibernate], geraremos a DLL a partir da camada [dao] da seguinte maneira:

  • [1]; os programas de teste são excluídos da compilação do projeto
  • [2,3], configuração do projeto
  • [4], geração do projeto
  • o arquivo DLL é gerado na pasta [bin/Release] [5]. Nós o adicionamos aos arquivos DLL já presentes na pasta [lib] [6]:

7.4. A camada de negócios

Voltemos à arquitetura geral do aplicativo [SimuPaie]:

Consideramos agora que a camada [dao] está implementada e que foi encapsulada na DLL [pam-dao-nhibernate.dll]. Agora, vamos nos concentrar na camada [metier]. É ela que implementa as regras de negócio, neste caso, as regras de cálculo de um salário.

7.4.1. O projeto do Visual Studio “ ” da camada [metier]

O projeto do Visual Studio da camada de negócios poderia ter a seguinte aparência:

  • em [1], todo o projeto configurado pelo arquivo [App.config]
  • em [2], a camada [metier] é composta pelas duas pastas [entites, service]. A pasta [tests] contém um programa de teste de console (Main.cs) e um programa de teste NUnit (NUnit.cs).
  • No [3], encontram-se as referências utilizadas pelo projeto. Destacam-se o DLL e o [pam-dao-nhibernate] da camada [dao] analisada anteriormente.

7.4.2. A interface [IPamMetier] da camada [metier]

Voltemos à arquitetura geral do aplicativo:

Que interface a camada [metier] deve oferecer à camada [ui]? Quais são as interações possíveis entre essas duas camadas? Lembremo-nos da interface web que será apresentada ao usuário:

  1. na exibição inicial do formulário, deve constar na camada [1] a lista de funcionários. Basta uma lista simplificada (Sobrenome, Nome, SS). O número SS é necessário para acessar as informações complementares sobre o funcionário selecionado (informações 6 a 11).
  2. As informações 12 a 15 correspondem às diferentes alíquotas de contribuição.
  3. As informações 16 a 19 são os subsídios vinculados ao índice do funcionário
  4. As informações 20 a 24 são os elementos do salário calculados a partir das entradas 1 a 3 feitas pelo usuário.

A interface [IPamMetier] fornecida à camada [ui] pela camada [metier] deve atender aos requisitos acima. Existem várias interfaces possíveis. Propomos a seguinte:


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

namespace Pam.Metier.Service {
    public interface IPamMetier {
        // lista de todas as identidades dos funcionários 
        Employe[] GetAllIdentitesEmployes();

        // ------- cálculo do salário 
        FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
    }
}
  • linha 7: o método que permitirá o preenchimento do combo [1]
  • linha 10: o método que permitirá obter as informações de 6 a 24. Estas foram reunidas em um objeto do tipo [FeuilleSalaire].

7.4.3. As entidades da camada [metier]

A pasta [entites] do projeto do Visual Studio contém os objetos manipulados pela classe de negócios: [FeuilleSalaire] e [ElementsSalaire].

A classe [FeuilleSalaire] contém as informações de 6 a 24 do formulário anterior:


using Pam.Dao.Entites;

namespace Pam.Metier.Entites {

    public class FeuilleSalaire {

        // propriedades 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);
        }
    }
}
  • linha 8: as informações de 6 a 11 sobre o funcionário cujo salário está sendo calculado e as informações de 16 a 19 sobre seus subsídios. É importante lembrar que um objeto [Employe] encapsula um objeto [Indemnites] que representa os subsídios do funcionário.
  • linha 9: as informações 12 a 15
  • linha 10: as informações 20 a 24
  • linhas 13-15: o método [ToString]

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


namespace Pam.Metier.Entites {
    public class ElementsSalaire {
        // propriedades 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);
        }
    }
}
  • linhas 4-8: os elementos do salário, conforme explicado nas regras de negócio descritas no parágrafo 3.2.
  • linha 4: o salário-base do funcionário, em função do número de horas trabalhadas
  • linha 5: as contribuições deduzidas desse salário-base
  • linhas 6 e 7: os adicional a serem somados ao salário-base, em função do índice do funcionário e do número de dias trabalhados
  • linha 8: o salário líquido a ser pago
  • linhas 12-15: o método [ToString] da classe.

7.4.4. Implementação da camada [metier]

Vamos implementar a interface [IPamMetier] com duas classes:

  • [AbstractBasePamMetier], que é uma classe abstrata na qual implementaremos o acesso aos dados da interface [IPamMetier]. Essa classe terá uma referência à camada [dao].
  • [PamMetier], uma classe derivada de [AbstractBasePamMetier], que, por sua vez, implementará as regras de negócio da interface [IPamMetier]. Ela 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 {

        // objeto de acesso aos dados 
        public IPamDao PamDao { get; set; }

        // lista de todas as identidades dos funcionários 
        public Employe[] GetAllIdentitesEmployes() {
            return PamDao.GetAllIdentitesEmployes();
        }

        // um funcionário específico com seus benefícios 
        protected Employe GetEmploye(string ss) {
            return PamDao.GetEmploye(ss);
        }

        // as contribuições 
        protected Cotisations GetCotisations() {
            return PamDao.GetCotisations();
        }

        // o cálculo do salário 
        public abstract FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
    }
}
  • linha 5: a classe pertence ao espaço de nomes [Pam.Metier.Service], assim como todas as classes e interfaces da camada [metier].
  • linha 6: a classe é abstrata (atributo abstract) e implementa a interface [IPamMetier]
  • linha 9: a classe possui uma referência à camada [dao] na forma de uma propriedade pública
  • linhas 12-14: implementação do método [GetAllIdentitesEmployes] da interface [IPamMetier] – utiliza o método de mesmo nome da camada [dao]
  • linhas 17-19: método interno (protected) [GetEmploye] que chama o método de mesmo nome da camada [dao] – declarado como protected para que as classes derivadas possam acessá-lo sem que ele seja público.
  • linhas 22-24: método interno (protected) [GetCotisations], que chama o método de mesmo nome da camada [dao]
  • linha 27: implementação abstrata (atributo abstract) do método [GetSalaire] 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 {

        // cálculo do salário 
        public override FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés) {
            // SS: nº SS do funcionário 
            // HeuresTravaillées: número de horas trabalhadas 
            // Dias Trabalhados: número de dias trabalhados 
            // recupera-se o funcionário com seus subsídios 
            ...
            // recupera-se as diversas alíquotas de contribuição 
            ...
            // calcula-se os componentes do salário 
            ...
            // gerar a folha de pagamento 
            return ...;
        }
    }
}
  • linha 7: a classe deriva de [AbstractBasePamMetier] e, portanto, implementa a interface [IPamMetier]
  • linha 10: o método [GetSalaire] a ser implementado

Pergunta: escreva o código do método [GetSalaire].


7.4.5. O teste de console da camada [metier]

Recordemos o projeto do Visual Studio da camada [metier]:

O programa de teste [Main] acima testa os métodos da interface [IPamMetier]. Um exemplo básico poderia ser o seguinte:


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 {
                // instanciação da camada [metier]
                IPamMetier pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
                // cálculos das folhas de pagamento 
                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();
        }
    }
}
  • linha 11: instanciação pela Spring da camada [metier].
  • linhas 13-14: testes do método [GetSalaire] da interface [IPamMetier]
  • linhas 15-22: teste do método [GetSalaire] quando ocorre uma exceção

O programa de teste utiliza o arquivo de configuração [App.config] a seguir:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- seções de configuração -->
    <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>


    <!-- configuração do 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>

    <!-- configuração NHibernate -->
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
....
    </hibernate-configuration>

    <!-- Esta seção contém as configurações do log4net -->
    <!-- NOTE IMPORTANTE: os logs não estão ativos por padrão. É necessário ativá-los por meio do programa
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
    <log4net>
...
    </log4net>

</configuration>

Esse arquivo é idêntico ao arquivo [App.config] utilizado para o projeto da camada [dao] (ver parágrafo 7.3.2), com as seguintes diferenças:

  • linha 20: o objeto com ID “pamdao” tem o tipo [Pam.Dao.Service.PamDaoNHibernate] e está localizado no conjunto [pam-dao-nhibernate]. A camada [dao] é a que foi analisada anteriormente.
  • linhas 21-23: o objeto com ID “pammetier” tem o tipo [Pam.Metier.Service.PamMetier] e está localizado no assembly [pam-metier-dao-nhibernate]. É necessário configurar o projeto da seguinte forma:
 
  • linha 22: o objeto [PamMetier] instanciado pelo Spring possui uma propriedade pública [PamDao], que é uma referência à camada [dao]. Essa propriedade é inicializada com a referência da camada [dao] criada na linha 20.

A execução realizada com o banco de dados descrito no parágrafo 6.2 produz o seguinte resultado no console:

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: as duas folhas de pagamento solicitadas
  • linha 3: a exceção do tipo [PamException] causada por um funcionário inexistente.

7.4.6. Testes unitários da camada de negócios

O teste anterior era visual: verificávamos na tela se os resultados esperados estavam sendo obtidos. Passamos agora aos testes não visuais NUnit.

Voltemos ao projeto do Visual Studio do projeto [metier]:

  • em [1], o programa de teste NUnit
  • para [2], a referência em DLL [nunit.framework]
  • em [3,4], a geração do projeto produzirá o DLL e o [pam-metier-dao-nhibernate.dll].
  • no [5], o arquivo [NUnit.cs] será incluído no assembly [pam-metier-dao-nhibernate.dll], mas não no [Main.cs] nem no [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 {

        // a camada [metier] a ser testada 
        private IPamMetier pamMetier;

        // construtor
        public NunitTestPamMetier() {
            // instanciação da camada [dao]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        }


        [Test]
        public void GetAllIdentitesEmployes() {
            // verificação do número de funcionários 
            Expect(2, EqualTo(pamMetier.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetSalaire1() {
            // cálculo de uma folha de pagamento 
            FeuilleSalaire feuilleSalaire = pamMetier.GetSalaire("254104940426058", 150, 20);
            // verificações 
            Expect(368.77, EqualTo(feuilleSalaire.ElementsSalaire.SalaireNet).Within(1E-06));
            // folha de pagamento de um funcionário inexistente 
            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 de acesso à camada [metier]. Observe-se que o tipo desse campo é uma interface e não uma classe. Isso 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] usando o Spring e o arquivo de configuração [App.config].
  • linhas 23-26: testam o método [GetAllIdentitesEmployes]
  • linhas 29-42: testam o método [GetSalaire]

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

A pasta [bin/Release] contém, além disso:

  • os arquivos DLL que fazem parte das referências do projeto e cujo atributo [Copie locale] está definido como verdadeiro: [Spring.Core, MySql.data, NHibernate, log4net, pam-dao-nhibernate]. Esses DLL vêm acompanhados de cópias dos DLL que eles próprios utilizam:
    • [CastleDynamicProxy, Iesi.Collections] para a ferramenta NHibernate
    • [antlr.runtime, Common.Logging] para a ferramenta Spring
  • o arquivo [pam-metier-dao-nhibernate.dll.config] é uma cópia do arquivo de configuração [App.config].

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

Image

Acima, os testes foram bem-sucedidos.

Trabalho prático:


  • executar na máquina os testes da classe [PamMetier].
  • utilizar diferentes arquivos de configuração App.config para usar diferentes SGBD (Firebird, MySQL, Postgres, SQL Server)

7.4.7. Geração do DLL da camada [metier]

Após a criação e o teste da classe [PamMetier], geraremos a DLL e a [pam-metier-dao-nhibernate.dll] a partir da camada [metier], seguindo o método descrito no parágrafo 7.3.5 Deve-se tomar o cuidado de não incluir na DLL os programas de teste [Main.cs] e [NUnit.cs]. Em seguida, ela será colocada na pasta [lib], que contém as pastas DLL e [1].

7.5. A camada [web]

Voltemos à arquitetura geral do aplicativo [SimuPaie]:

Consideramos que as camadas [dao] e [métier] já estão implementadas e encapsuladas nas camadas DLL e [pam-dao-nhibernate, pam-metier-dao-nhibernate.dll]. Passaremos agora a descrever a camada web.

7.5.1. O projeto Visual Web Developer da camada [web]

  • em [1], o projeto como um todo:
    • [Global.asax]: a classe instanciada ao iniciar o aplicativo web e que garante a inicialização do aplicativo
    • [Default.aspx]: a página do formulário web
  • em [2], os DLL necessários para a aplicação web. Destacam-se os DLL das camadas [dao] e [metier] criadas anteriormente.

7.5.2. Configuração da aplicação

O arquivo [Web.config], que configura a aplicação, define os mesmos dados que o arquivo [App.config], que configura a camada [metier] analisada anteriormente. Esses dados devem ser inseridos no código pré-gerado do arquivo [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>

  <!-- configuração do 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>

  <!-- configuração 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 seção contém as configurações do log4net -->
  <!-- NOTE IMPORTANTE: os logs não estão ativos por padrão. É necessário ativá-los por meio do programa
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
  <log4net>
....
  </log4net>

  <appSettings/>
  <connectionStrings/>

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

</configuration>

Nas linhas 9-12, 18-28 e 31-44, encontramos a configuração do Spring e do NHibernate descrita no arquivo [App.config] da camada [metier] (ver parágrafo 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
  {
    // --- dados estáticos do aplicativo ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;
    public static string Msg;
    public static bool Erreur = false;

    // inicialização do aplicativo
    public void Application_Start(object sender, EventArgs e)
    {
      // processamento do arquivo de configuração
      try
      {
        // instanciação da camada [metier]
        PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        // lista simplificada de funcionários
        Employes = PamMetier.GetAllIdentitesEmployes();
        // Operação bem-sucedida
        Msg = "Base chargée...";
      }
      catch (Exception ex)
      {
        // registrando o erro
        Msg = string.Format("L'erreur suivante s'est produite lors de l'accès à la base de données : {0}", ex);
        Erreur = true;
      }
    }
  }
}

Lembre-se de que:

  • a classe [Global.asax.cs] é instanciada no início da aplicação e que essa instância está acessível a todas as consultas de todos os usuários. Os campos estáticos das linhas 11 a 14 são, portanto, compartilhados entre todos os usuários.
  • o método [Application_Start] é executado uma única vez após a instanciação da classe. É nesse método que geralmente é feita a inicialização do aplicativo.

Os dados compartilhados por todos os usuários são os seguintes:

  • linha 11: a matriz de objetos do tipo [Employe] que armazenará a lista simplificada (SS, NOM, PRENOM) de todos os funcionários
  • linha 12: uma referência à camada [metier] encapsulada na DLL [pam-metier-dao-nhibernate.dll]
  • linha 13: uma mensagem indicando o resultado da inicialização (bem-sucedida ou com erro)
  • linha 14: um valor booleano indicando se a inicialização terminou com erro ou não.

No [Application_Start]:

  • linha 23: o Spring instancia as camadas [metier] e [dao] e retorna uma referência à camada [metier]. Essa referência é armazenada no campo estático [PamMetier] da linha 12.
  • linha 25: a tabela de funcionários é solicitada à camada [metier]
  • linha 27: a mensagem em caso de sucesso
  • linha 32: a mensagem em caso de erro

7.5.3. O formulário [Default.aspx]

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

Image


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


Trabalho prático:


  • implementar no servidor o aplicativo web anterior
  • utilizar diferentes arquivos de configuração [Web.config] para usar diferentes SGBD (Firebird, MySQL, Postgres, SQL Server)