Skip to content

7. Caso de estudo com o Firebird 2.1

7.1. Instalação das ferramentas

As ferramentas a instalar são as seguintes:

  • o SGBD: [http://www.firebirdsql.org/en/firebird-2-1-5/];
  • uma ferramenta de administração: EMS, SQL Manager para InterBase/Firebird Freeware, [http://www.sqlmanager.net/fr/products/ibfb/manager/download].

Nos exemplos que se seguem, o utilizador é o sysdba com a palavra-passe masterkey.

Iniciemos o Firebird e, em seguida, a ferramenta [SQL Manager Lite for Firebird], com a qual iremos administrar o SGBD.

  • No [1], iniciamos o Firebird SGBD a partir do Menu Iniciar. Aqui, o SGBD não foi instalado como um serviço do Windows;
  • em [2], o serviço é iniciado. Aparece um ícone no canto inferior direito do ecrã. Com um clique direito nesse ícone, é possível parar o SGBD.

Vamos agora iniciar a ferramenta [SQL Manager Lite for Firebird], com a qual iremos administrar o SGBD e o [3].

  • no [4], criamos uma nova base de dados;
  • no [5], confirmamos;
  • em [5], iniciamos sessão como SYSDBA / masterkey;
  • em [6], indicamos a localização do ficheiro que vai ser criado. Com efeito, a base de dados vai ser criada num único ficheiro;
  • em [7], valida-se a ordem SQL que vai ser executada;
  • em [8], a base de dados foi criada. Deve agora ser gravada em [EMS Manager]. As informações estão corretas. Executa-se [OK];
  • no [9], ligamo-nos à base;
  • em [10], o [EMS Manager] apresenta a base de dados, por enquanto vazia.

Vamos agora ligar um projeto VS 2012 a esta base de dados.

7.2. Criação da base a partir das entidades

Começamos por duplicar a pasta do projeto [RdvMedecins-SqlServer-01] em [RdvMedecins-Firebird-01] e [1]:

  • em [2]; em VS 2012, eliminamos o projeto [RdvMedecins-SqlServer-01] da solução;
  • em [3], o projeto foi eliminado;
  • em [4], adicionamos outro. Este está incluído na pasta [RdvMedecins-Firebird-01] que criámos anteriormente;
  • em [5], o projeto carregado chama-se [RdvMedecins-SqlServer-01];
  • em [6], alteramos o seu nome para [RdvMedecins-Firebird-01]
  • em [7], adiciona-se outro projeto à solução. Este encontra-se na pasta [RdvMedecins-SqlServer-01] do projeto que eliminámos anteriormente da solução;
  • em [8], o projeto [RdvMedecins-SqlServer-01] foi reintegrado na solução.

O projeto [RdvMedecins-Firebird-01] é idêntico ao projeto [RdvMedecins-SqlServer-01]. Temos de fazer algumas alterações. No [App.config], vamos alterar a cadeia de ligação e o [DbProviderFactory], que temos de adaptar a cada SGBD.


<!-- cadeia de ligação à base de dados -->
  <connectionStrings>
    <add name="monContexte" connectionString="User=SYSDBA;Password=masterkey;Database=D:\data\istia-1213\c#\dvp\Entity Framework\databases\firebird\RDVMEDECINS-EF.GDB;DataSource=localhost;
Port=3050;Dialect=3;Charset=NONE;Role=;Connection lifetime=15;Pooling=true;MinPoolSize=0;MaxPoolSize=50;Packet Size=8192;ServerType=0;" providerName="FirebirdSql.Data.FirebirdClient" />
  </connectionStrings>
  <!-- o provedor de fábrica -->
  <system.data>
    <DbProviderFactories>
      <add name="Firebird Client Data Provider" invariant="FirebirdSql.Data.FirebirdClient" description=".Net Framework Data Provider for Firebird" type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory, FirebirdSql.Data.FirebirdClient, Version=2.7.7.0, Culture=neutral, PublicKeyToken=3750abcc3150b00c" />
    </DbProviderFactories>
  </system.data>
  • linha 3: o utilizador e a sua palavra-passe, bem como o caminho completo da base de dados Firebird;
  • linhas 8-10: o DbProviderFactory. A linha 9 faz referência a um DLL [FirebirdSql.Data.FirebirdClient] que não temos. Pode-se obtê-lo com o NuGet e o [1]:
  • no [2], na área de pesquisa, introduz-se a palavra-chave firebird;
  • em [3], selecione o pacote [Firebird ADO.NET Data Provider]. Trata-se de um conector ADO.NET para o Firebird;
  • em [4], a nova referência;
  • em [5], em [App.config], é necessário indicar a versão correta do DLL. Esta encontra-se nas suas propriedades.

No ficheiro [Entites.cs], é necessário adaptar o esquema das tabelas que vão ser geradas:


  [Table("MEDECINS")]
  public class Medecin : Personne
  {...}

  [Table("CLIENTS")]
  public class Client : Personne
  {...}

  [Table("CRENEAUX")]
  public class Creneau
  {...}

  [Table("RVS")]
  public class Rv
  {...}

Aqui, as tabelas não têm esquema.

Configuramos a execução do projeto:

  • em [1], atribuímos outro nome ao assembly que vai ser gerado;
  • em [2], definimos também outro espaço de nomes por predefinição;
  • em [3], indicamos o programa a executar.

Nesta fase, não há erros de compilação. Vamos executar o programa [CreateDB_01]. Obtém-se a seguinte exceção:

Exception non gérée : System.Data.MetadataException: Le schéma spécifié n'est pas valide. Erreurs :
(11,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(23,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(33,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(43,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
   à System.Data.Metadata.Edm.StoreItemCollection.Loader.ThrowOnNonWarningErrors
()
   ...
   à RdvMedecins_01.CreateDB_01.Main(String[] args) dans d:\data\istia-1213\c#\d
vp\Entity Framework\RdvMedecins\RdvMedecins-Oracle-01\CreateDB_01.cs:ligne 15

Lembramo-nos de ter tido o mesmo erro com o MySQL, o Oracle e o PostgreSQL. Está relacionado com o tipo do campo Timestamp das entidades. Fazemos a mesma alteração que fizemos nos dois SGBD anteriores. Nas entidades, substituímos as três linhas


    [Column("TIMESTAMP")]
    [Timestamp]
    public byte[] Timestamp { get; set; }

pelas seguintes:


    [ConcurrencyCheck]
    [Column("VERSIONING")]
    public int? Versioning { get; set; }

Alteramos, portanto, o tipo da coluna, que passa de byte[] para int?. No SGBD, utilizaremos procedimentos armazenados para incrementar este inteiro em uma unidade sempre que uma linha for inserida ou alterada.

Efetuaremos a alteração anterior nas quatro entidades e, em seguida, voltaremos a executar a aplicação. Obteremos então o seguinte erro:

1
2
3
4
5
Exception non gérée : FirebirdSql.Data.FirebirdClient.FbException: lock time-out on wait transaction object D:\DATA\ISTIA-1213\C#\DVP\ENTITY FRAMEWORK\DATABASES\FIREBIRD\RDVMEDECINS-EF.GDB está em uso ---> FirebirdSql.Data.Common.IscException: tempo limite de bloqueio na transação em espera
object D:\DATA\ISTIA-1213\C#\DVP\ENTITY FRAMEWORK\DATABASES\FIREBIRD\RDVMEDECINS
-EF.GDB is in use
...
   à RdvMedecins_01.CreateDB_01.Main(String[] args) dans d:\data\istia-1213\c#\dvp\Entity Framework\RdvMedecins\RdvMedecins-Firebird-01\CreateDB_01.cs:linha 15

A linha 1 indica que a base de dados está a ser utilizada. Parece-me que não era esse o caso e não consegui resolver este problema.

Não importa. Vamos criar a base de dados [RDVMEDECINS-EF] manualmente com a ferramenta [EMS Manager for Firebird]. Não descrevemos todos os passos, mas apenas os mais importantes.

A base de dados Firebird terá a seguinte estrutura:

As tabelas

  • em [1], ID é uma chave primária com o atributo Autoincrement. Será gerada automaticamente pelo SGBD;

As diferentes tabelas têm as chaves primárias e estrangeiras que essas mesmas tabelas tinham nos exemplos anteriores. As chaves estrangeiras têm o atributo ON, DELETE e CASCADE.

Os geradores

Tal como no Oracle e no PostgreSQL, criámos geradores de números consecutivos. Existem 5: [1].

  • [CLIENTS_ID_GEN] será utilizado para gerar a chave primária da tabela [CLIENTS];
  • [MEDECINS_ID_GEN] será utilizado para gerar a chave primária da tabela [MEDECINS];
  • [CRENEAUX_ID_GEN] será utilizado para gerar a chave primária da tabela [CRENEAUX];
  • [RVS_ID_GEN] será utilizado para gerar a chave primária da tabela [RVS];
  • [VERSIONS_GEN] será utilizado para gerar os valores das colunas [VERSIONING] de todas as tabelas.

Os gatilhos

Um trigger é um procedimento executado pelo SGBD antes ou depois de um evento (inserção, modificação, eliminação) numa tabela. Temos 8 [1]:

Vejamos o código DDL do trigger [BI_CLIENTS_ID] que preenche a coluna [ID] da tabela [CLIENTS]:

1
2
3
4
5
6
7
8
CREATE TRIGGER BI_CLIENTS_ID FOR CLIENTS
ACTIVE BEFORE INSERT
POSITION 0
AS
BEGIN
  IF (NEW.ID IS NULL) THEN
      NEW.ID = GEN_ID(CLIENTS_ID_GEN, 1);
END^
  • linha 2: antes de cada inserção na tabela [CLIENTS];
  • linhas 6-7: se a coluna ID for NULL, então atribui-se-lhe o valor seguinte do gerador de números [CLIENTS_ID_GEN].

Os gatilhos [ BI_CLIENTS_ID, BI_MEDECINS_ID, BI_CRENEAUX_ID, BI_RVS_ID] são todos construídos da mesma forma.

Vejamos agora o código DDL do trigger [CLIENTS_VERSION_TRIGGER], que alimenta a coluna [VERSIONING] da tabela [CLIENTS]:

1
2
3
4
5
6
7
CREATE TRIGGER CLIENTS_VERSION_TRIGGER FOR CLIENTS
ACTIVE BEFORE INSERT OR UPDATE
POSITION 1
AS
BEGIN
  NEW."VERSIONING" = GEN_ID(VERSIONS_GEN,1);
END^
  • linhas 1-3: antes de cada operação INSERT ou UPDATE na tabela [CLIENTS];
  • linha 6: a coluna ["VERSIONING"] recebe o seguinte valor do gerador de números [VERSIONS_GEN]. Este gerador alimenta as colunas ["VERSIONING"] das quatro tabelas.

Os gatilhos [MEDECINS_VERSION_TRIGGER, CRENEAUX_VERSION_TRIGGER, RVS_VERSION_TRIGGER] são semelhantes.

O script de geração das tabelas da base de dados Firebird [RDVMEDECINS-EF] foi colocado na pasta [RdvMedecins / databases / Firebird]. O leitor poderá carregá-lo e executá-lo para criar as suas tabelas.

Feito isto, os diferentes programas do projeto podem ser executados. Estes produzem os mesmos resultados que com o SQL Server, exceto no caso do programa [ModifyDetachedEntities], que entra em falha pela mesma razão que tinha entrado em falha com o Oracle e o MySQL. O problema resolve-se da mesma forma. Basta copiar o programa [ModifyDetachedEntities] do projeto [RdvMedecins-Oracle-01] para o projeto [RdvMedecins-Firebird-01].

7.3. Arquitetura multicamadas baseada em EF 5

Voltamos ao nosso caso de estudo descrito no parágrafo 2.

Vamos começar por construir a camada [DAO] de acesso aos dados. Para tal, duplicamos o projeto de consola VS 2012 [RdvMedecins-SqlServer-02] em [RdvMedecins-Firebird-02] [1]:

  • em [2], eliminamos o projeto [RdvMedecins-SqlServer-02];
  • em [3], adiciona-se um projeto existente à solução. Este é retirado da pasta [RdvMedecins-Firebird-02] que acabou de ser criada;
  • em [4], o novo projeto tem o mesmo nome do que foi eliminado. Vamos alterar o seu nome;
  • em [5], alterámos o nome do projeto;
  • em [6], alteramos algumas das suas propriedades, como, neste caso, o nome do assembly;
  • em [7], a pasta [Models] é eliminada para ser substituída pela pasta [Models] do projeto [RdvMedecins-Firebird-01]. Com efeito, os dois projetos partilham os mesmos modelos.
  • em [8], as referências atuais do projeto;
  • no [9], foi adicionado o conector ADO.NET do Firebird com a ferramenta NuGet.

No ficheiro [App.config], substituem-se as informações da base de dados SQL Server pelas da base de dados Firebird. Estas encontram-se no ficheiro [App.config] do projeto [RdvMedecins-Firebird-01]:


<!-- cadeia de ligação à base de dados -->
  <connectionStrings>
    <add name="monContexte" connectionString="User=SYSDBA;Password=masterkey;Database=D:\data\istia-1213\c#\dvp\Entity Framework\databases\firebird\RDVMEDECINS-EF.GDB;DataSource=localhost;
Port=3050;Dialect=3;Charset=NONE;Role=;Connection lifetime=15;Pooling=true;MinPoolSize=0;MaxPoolSize=50;Packet Size=8192;ServerType=0;" providerName="FirebirdSql.Data.FirebirdClient" />
  </connectionStrings>
  <!-- o fornecedor de fábrica -->
  <system.data>
    <DbProviderFactories>
      <add name="Firebird Client Data Provider" invariant="FirebirdSql.Data.FirebirdClient" description=".Net Framework Data Provider for Firebird" type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory, FirebirdSql.Data.FirebirdClient, Version=2.7.7.0, Culture=neutral, PublicKeyToken=3750abcc3150b00c" />
    </DbProviderFactories>
  </system.data>

Os objetos geridos pelo Spring também se alteram. Atualmente, temos:


  <!-- configuração do Spring -->
  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object id="rdvmedecinsDao" type="RdvMedecins.Dao.Dao,RdvMedecins-SqlServer-02" />
    </objects>
</spring>

A linha 7 faz referência ao assembly do projeto [RdvMedecins-SqlServer-02]. O assembly é agora [RdvMedecins-Firebird-02].

Feito isto, estamos prontos para executar o teste da camada [DAO]. Antes disso, é necessário preencher a base de dados (programa [Fill] do projeto [RdvMedecins-Firebird-01]). O programa de teste é bem-sucedido.

Criamos a DLL do projeto, tal como foi feito para o projeto [RdvMedecins-SqlServer-02], e reunimos oconjunto de DLL do projeto numa pasta [lib] criada em [RdvMedecins-Firebird-02]. Estas serão as referências do projeto web [RdvMedecins-Firebird-03] que se seguirá.

  

Estamos agora prontos para construir a camada [ASP.NET] da nossa aplicação:

Vamos partir do projeto [RdvMedecins-SqlServer-03]. Duplicamos a pasta deste projeto em [RdvMedecins-Firebird-03] e [1]:

  • em [2], com o VS 2012 Express para a Web, abrimos a solução da pasta [RdvMedecins-Firebird-03];
  • em [3], alteramos tanto o nome da solução como o nome do projeto;
  • em [4], as referências atuais do projeto;
  • em [5], eliminamo-las;
  • em [6], para as substituir por referências ao DLL que acabámos de guardar numa pasta [lib] do projeto [RdvMedecins-Firebird-02].

Resta-nos apenas modificar o ficheiro [Web.config]. Substituímos o seu conteúdo atual pelo conteúdo do ficheiro [App.config] do projeto [RdvMedecins-Firebird-02]. Feito isto, executamos o projeto web. Funciona. Não nos esqueçamos de preencher a base de dados antes de executar a aplicação web.