6. Estudo de caso com PostgreSQL 9.2.1
6.1. Instalação das ferramentas
As ferramentas a serem instaladas são as seguintes:
- o SGBD: [http://www.enterprisedb.com/products-services-training/pgdownload#windows];
- uma ferramenta de administração: EMS, SQL Manager para PostgreSQL, Freeware [http://www.sqlmanager.net/fr/products/postgresql/manager/download].
Nos exemplos a seguir, o usuário postgres tem a senha postgres.
Vamos executar o PostgreSQL e, em seguida, a ferramenta [SQL Manager Lite for PostgreSQL], com a qual administraremos o SGBD.
![]() |
- no [1], iniciamos o SGBD e o PostgreSQL a partir dos serviços do Windows;
- no [2], o serviço é iniciado;
Agora iniciamos a ferramenta [SQL Manager Lite for MySQL], com a qual administraremos o SGBD e o [3].
![]() |
- no [4], criamos um novo banco de dados;
- no [5], indicamos o nome do banco de dados;
![]() |
- em [5], conectamo-nos como postgres / postgres;
- em [6], fornecemos algumas informações;
- em [7], confirma-se a ordem SQL que será executada;
![]() |
- no [8], o banco de dados foi criado. Agora ele deve ser registrado no [EMS Manager]. As informações estão corretas. Executa-se o [OK];
- no [9], conectamo-nos a ela;
- em [10], [EMS Manager] exibe o banco de dados, que por enquanto está vazio. Observe que as tabelas pertencerão a um esquema chamado public [11].
Agora vamos conectar um projeto VS 2012 a esse banco de dados.
6.2. Criação do banco de dados a partir das entidades
Começamos duplicando a pasta do projeto [RdvMedecins-SqlServer-01] em [RdvMedecins-PostgreSQL-01] e [1]:
![]() |
- em [2]; em VS 2012, excluímos o projeto [RdvMedecins-SqlServer-01] da solução;
![]() |
- em [3], o projeto foi excluído;
- em [4], adicionamos outro. Este está contido na pasta [RdvMedecins-PostgreSQL-01] que criamos anteriormente;
![]() |
- em [5], o projeto carregado se chama [RdvMedecins-SqlServer-01];
- em [6], alteramos o nome para [RdvMedecins-PostgreSQL-01];
![]() |
- em [7], adiciona-se outro projeto à solução. Esse projeto está na pasta [RdvMedecins-SqlServer-01] do projeto que havíamos excluído da solução anteriormente;
- em [8], o projeto [RdvMedecins-SqlServer-01] foi reintegrado à solução.
O projeto [RdvMedecins-PostgreSQL-01] é idêntico ao projeto [RdvMedecins-SqlServer-01]. Precisamos fazer algumas alterações. No [App.config], vamos alterar a string de conexão e o [DbProviderFactory], que deve ser adaptado a cada SGBD.
<!-- cadeia de conexão com o banco de dados -->
<connectionStrings>
<add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<!-- o provedor de fábrica -->
<system.data>
<DbProviderFactories>
<add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
</DbProviderFactories>
</system.data>
- linha 3: o usuário e sua senha;
- linhas 7-9: o DbProviderFactory. A linha 8 faz referência a um DLL e a um [Npgsql] que não temos. Ela é obtida com NuGet e [1]:
![]() |
- em [2], na área de pesquisa digita-se a palavra-chave postgresql;
- em [3], selecione o pacote [Npgsql]. Trata-se de um conector ADO.NET para PostgreSQL;
![]() |
- no [4], foram adicionadas duas referências;
- no [5], no [App.config], é preciso inserir a versão correta do DLL. Ela pode ser encontrada nas propriedades do arquivo.
No arquivo [Entites.cs], é preciso ajustar o esquema das tabelas que serão geradas:
[Table("MEDECINS", Schema = "public")]
public class Medecin : Personne
{...}
[Table("CLIENTS", Schema = "public")]
public class Client : Personne
{...}
[Table("CRENEAUX", Schema = "public")]
public class Creneau
{...}
[Table("RVS", Schema = "public")]
public class Rv
{...}
Vimos anteriormente, durante a criação de uma base de dados PostgreSQL, que as tabelas pertenciam a um esquema chamado “public”.
Configuramos a execução do projeto:
![]() |
- em [1], atribuímos outro nome ao assembly que será gerado;
- em [2], definimos também outro namespace padrão;
- em [3], indicamos o programa a ser executado.
Nesta fase, não há erros de compilação. Vamos executar o programa [CreateDB_01]. Recebemos a seguinte exceção:
Lembramos de ter tido o mesmo erro com MySQL e o Oracle. Isso está relacionado ao tipo do campo Timestamp das entidades. Fazemos a mesma modificação que fizemos com o Oracle. 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 o tipo da coluna de byte[] para int?. No SGBD, utilizaremos procedimentos armazenados para incrementar esse inteiro em uma unidade sempre que uma linha for inserida ou modificada.
Fazemos a modificação anterior nas quatro entidades e, em seguida, reexecutamos o aplicativo. Recebemos então o seguinte erro:
A linha 1 indica que o conector ADO.NET do PostgreSQL não é capaz de excluir o banco de dados existente. Exatamente como no Oracle. Somos, então, levados a criar manualmente o banco de dados [RDVMEDECINS-EF] com a ferramenta [EMS Manager for PostgreSQL]. Não descreveremos todas as etapas, mas apenas as mais importantes.
O banco de dados PostgreSQL terá a seguinte estrutura:
As tabelas
![]() |
- em [1], ID é a chave primária do tipo serial. Esse tipo PostgreSQL é um inteiro gerado automaticamente pelo SGBD.
![]() |
![]() |
![]() |
![]() |
As diferentes tabelas possuem as chaves primárias e estrangeiras que essas mesmas tabelas tinham nos exemplos anteriores. As chaves estrangeiras possuem o atributo ON, DELETE e CASCADE.
As sequências
Assim como no Oracle, criamos aqui sequências. Elas são geradores de números consecutivos. São 5: [1].
![]() |
- em [2], vemos as propriedades da sequência [CLIENTS_ID_SEQ]. Ela gera números consecutivos de 1 em 1, começando em 1 até um valor muito grande.
Todas as sequências são construídas seguindo o mesmo modelo.
- [CLIENTS_ID_seq] será usada para gerar a chave primária da tabela [CLIENTS];
- [MEDECINS_ID_seq] será utilizada para gerar a chave primária da tabela [MEDECINS];
- [CRENEAUX_ID_seq] será usada para gerar a chave primária da tabela [CRENEAUX];
- [RVS_ID_seq] será utilizada para gerar a chave primária da tabela [RVS];
- [sequence_versions] será utilizada 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, exclusão) em uma tabela. Temos 4 triggers [1]:
![]() |
Vamos examinar o código DDL do gatilho [CLIENTS_tr] que alimenta a coluna [VERSIONING] da tabela [CLIENTS]:
- linhas 1-3: antes de cada operação INSERT ou UPDATE na tabela [CLIENTS];
- linha 4: o procedimento [public.trigger_versions()] é executado.
O procedimento [public.trigger_versions()] é o seguinte:
- linha 2: NEW representa a linha que será inserida ou modificada. NEW. “VERSIONING” é a coluna [VERSIONING] dessa linha. Atribui-se a ela o seguinte valor do gerador de números: “sequence_versions”. Assim, a coluna ["VERSIONING"] muda a cada operação INSERT / UPDATE realizada na tabela [CLIENTS].
Os gatilhos [MEDECINS_tr, CRENEAUX_tr, RVS_tr] funcionam de maneira semelhante. As quatro colunas ["VERSIONING"] obtêm seus valores da mesma sequência.
O script de geração das tabelas do banco de dados PostgreSQL e [RDVMEDECINS-EF] foi colocado na pasta [RdvMedecins / databases / postgreSQL]. O usuário poderá carregá-lo e executá-lo para criar suas tabelas.
Feito isso, os diversos programas do projeto podem ser executados. Eles fornecem os mesmos resultados que com o SQL Server, exceto pelo programa [ModifyDetachedEntities], que trava pelo mesmo motivo que travava com o Oracle. O problema é resolvido da mesma maneira. Basta copiar o programa [ModifyDetachedEntities] do projeto [RdvMedecins-Oracle-01] para o projeto [RdvMedecins-PostgreSQL-01].
O programa [LazyEagerLoading] trava com a seguinte exceção:
O código com erro é o seguinte:
using (var context = new RdvMedecinsContext())
{
// vaga n.º 0
creneau = context.Creneaux.Include("Medecin").Single<Creneau>(c => c.Id == idCreneau);
Console.WriteLine(creneau.ShortIdentity());
}
Na linha nº 1 da exceção, o erro relatado sugere uma junção, pois LEFT é uma palavra-chave da junção. Como a linha 4 do código acima solicita o carregamento imediato da dependência [Medecin] de uma entidade [Creneau], EF realizou uma junção entre as tabelas [CRENEAUX] e [MEDECINS]. No entanto, parece que o conector ADO.NET gerou uma ordem SQL incorreta. Reescrevemos o código da seguinte maneira:
using (var context = new RdvMedecinsContext())
{
// horário n.º 0
creneau = context.Creneaux.Find(idCreneau);
Console.WriteLine(creneau.ShortIdentity());
// forçamos o carregamento do médico associado
// isso é possível porque ainda estamos em um contexto aberto
Medecin medecin = creneau.Medecin;
}
- linha 4: buscamos o intervalo sem junção;
- linha 8: recuperamos a dependência que faltava.
Funciona. Mais uma vez, constatamos que a alteração em SGBD tem impacto no código. Na verdade, não é o SGBD que está em questão aqui, mas sim seu conector ADO.NET.
6.3. Arquitetura multicamadas baseada em EF 5
Voltamos ao nosso estudo de caso descrito no parágrafo 2.
![]() |
Começaremos construindo a camada [DAO] de acesso aos dados. Para isso, duplicamos o projeto de console VS 2012 [RdvMedecins-SqlServer-02] em [RdvMedecins-PostgreSQL-02] [1]:
![]() |
- em [2], excluímos o projeto [RdvMedecins-SqlServer-02];
![]() |
- em [3], adiciona-se um projeto existente à solução. Ele é obtido da pasta [RdvMedecins-PostgreSQL-02] que acaba de ser criada;
- em [4], o novo projeto tem o mesmo nome daquele que foi excluído. Vamos alterar seu nome;
![]() |
- em [5], alteramos o nome do projeto;
- em [6], alteramos algumas de suas propriedades, como, neste caso, o nome do assembly;
- em [7], a pasta [Models] é excluída para ser substituída pela pasta [Models] do projeto [RdvMedecins-PostgreSQL-01]. De fato, os dois projetos compartilham os mesmos modelos.
![]() |
- em [8], as referências atuais do projeto;
- no [9], foi adicionado o conector ADO.NET do PostgreSQL com a ferramenta NuGet.
No arquivo [App.config], substituímos as informações do banco de dados SQL Server pelas do banco de dados PostgreSQL. Elas podem ser encontradas no arquivo [App.config] do projeto [RdvMedecins-PostgreSQL-01]:
<!-- cadeia de conexão no banco de dados -->
<connectionStrings>
<add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<!-- o provedor de fábrica -->
<system.data>
<DbProviderFactories>
<add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
</DbProviderFactories>
</system.data>
Os objetos gerenciados pelo Spring também mudam. 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-PostgreSQL-02].
Feito isso, estamos prontos para executar o teste da camada [DAO]. Antes disso, é preciso preencher o banco de dados (programa [Fill] do projeto [RdvMedecins-PostgreSQL-01]). O programa de teste trava com a seguinte exceção:
Na linha 13, a mensagem indica que o erro ocorreu no método [GetCreneauxMedecin] da camada [DAO]. O código é o seguinte:
// lista de horários disponíveis de um determinado médico
public List<Creneau> GetCreneauxMedecin(int idMedecin)
{
// lista de horários
try
{
// abertura do contexto de persistência
using (var context = new RdvMedecinsContext())
{
// recupera-se o médico com seus horários
Medecin medecin = context.Medecins.Include("Creneaux").Single(m => m.Id == idMedecin);
// retorna a lista de horários do médico
return medecin.Creneaux.ToList<Creneau>();
}
}
catch (Exception ex)
{
throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
}
}
Na linha 11, reconhece-se a palavra-chave Include, que já causou a falha de um programa anterior. O código anterior pode ser substituído pelo seguinte:
// lista de horários de um determinado médico
public List<Creneau> GetCreneauxMedecin(int idMedecin)
{
// lista de horários
try
{
// abertura do contexto de persistência
using (var context = new RdvMedecinsContext())
{
// retorna a lista de horários do médico
return context.Creneaux.Where(c => c.MedecinId == idMedecin).ToList<Creneau>();
}
}
catch (Exception ex)
{
throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
}
}
O novo código parece até mais coerente do que o antigo. De qualquer forma, desta vez o programa de teste foi aprovado.
Criamos o DLL do projeto, da mesma forma que foi feito para o projeto [RdvMedecins-SqlServer-02], e reunimostodos os arquivos DLL do projeto em uma pasta [lib] criada dentro de [RdvMedecins-PostgreSQL-02]. Essas serão as referências do projeto web [RdvMedecins-PostgreSQL-03] que virá a seguir.
![]() |
Agora estamos prontos para construir a camada [ASP.NET] do nosso aplicativo:
![]() |
Vamos partir do projeto [RdvMedecins-SqlServer-03]. Duplicamos a pasta desse projeto em [RdvMedecins-PostgreSQL-03] e [1]:
![]() |
- em [2], com o VS 2012 Express para a web, abrimos a solução da pasta [RdvMedecins-PostgreSQL-03];
- em [3], alteramos tanto o nome da solução quanto o nome do projeto;
![]() |
- no [4], as referências atuais do projeto;
- em [5], as eliminamos;
- em [6], para substituí-las por referências ao DLL que acabamos de armazenar na pasta [lib] do projeto [RdvMedecins-PostgreSQL-02].
Resta apenas modificar o arquivo [Web.config]. Substituímos seu conteúdo atual pelo conteúdo do arquivo [App.config] do projeto [RdvMedecins-PostgreSQL-02]. Feito isso, executamos o projeto web. Ele funciona. Não se esqueça de preencher o banco de dados antes de executar o aplicativo web.


























