5. Estudo de caso com o Oracle Database Express Edition 11g Release 2
5.1. Instalação das ferramentas
As ferramentas a instalar são as seguintes:
- o SGBD: [http://www.oracle.com/technetwork/products/express-edition/downloads/index.html];
- uma ferramenta de administração: EMS SQL Manager for Oracle Freeware [http://www.sqlmanager.net/fr/products/oracle/manager/download];
- um cliente Oracle para .NET: ODAC 11.2 Release 5 (11.2.0.3.20) com Oracle Developer Tools para Visual Studio: [http://www.oracle.com/technetwork/developer-tools/visual-studio/downloads/index.html].
Nos exemplos seguintes, o utilizador «system» tem a palavra-passe «system».
Inicie o Oracle [1] e, em seguida, a ferramenta [SQL Manager Lite for Oracle], que utilizaremos para administrar o SGBD [2].
![]() |
- Em [3], estabelecemos ligação a uma base de dados existente;
![]() |
- Em [4], utilizamos o serviço Oracle XE para estabelecer a ligação;
- em [5], especificamos o nome da base de dados XE;
- em [6], iniciamos sessão como system/system;
- Em [7], concluímos o assistente;
![]() |
- em [8], ligamo-nos à base de dados;
- Em [9], estamos conectados;
- Uma vez que iniciámos sessão como o utilizador system/system, que possui privilégios alargados, podemos, por exemplo, gerir utilizadores [10];
![]() |
- Em [11], crie um novo utilizador;
- em [12], o utilizador será denominado [RDVMEDECINS-EF];
- em [13], e terá a palavra-passe rdvmedecins;
- Em [14], a criação do utilizador é confirmada;
- em [15], o utilizador foi criado;
![]() |
- em [16], o utilizador [RDVMEDECINS-EF] é também um esquema de base de dados;
- em [17], o utilizador tal como foi criado não tem permissões suficientes. Concedemo-las através de um script SQL;
![]() |
- em [18], o script é executado;
- em [19], vamos tentar iniciar sessão como [RDVMEDECINS-EF] para ver o que ele pode fazer. Para isso, começamos por registar uma nova base de dados no [EMS Manager];
![]() |
- em [19], iniciamos sessão através do serviço XE;
- em [20], iniciamos sessão como RDVMEDECINS-EF / rdvmedecins;
- Em [21], atribuímos um alias que reflete o nome do utilizador que iniciou sessão;
- em [22], ligamo-nos ao Oracle utilizando as credenciais fornecidas;
![]() |
![]() |
- Em [22], conseguimos iniciar sessão;
- Em [23], tentamos criar uma tabela no esquema [RDVMEDECINS-EF];
- em [24], definimos uma tabela;
- Em [25], validamos a sua definição;
![]() |
- Em [26], a tabela foi criada. Apagamo-la;
- Em [27], foi eliminada.
Agora que temos um utilizador com permissões suficientes, vamos criar o projeto VS 2012 que irá criar as tabelas no esquema [RDVMEDECINS-EF] com base nas definições das entidades.
5.2. Criação da base de dados a partir das entidades
Começamos por duplicar a pasta do projeto [RdvMedecins-SqlServer-01] para [RdvMedecins-Oracle-01] [1]:
![]() |
- em [2], no VS 2012, removemos o projeto [RdvMedecins-SqlServer-01] da solução;
![]() |
- em [3], o projeto foi removido;
- em [4], adicionamos outro. Este foi retirado da pasta [RdvMedecins-Oracle-01] que criámos anteriormente;
![]() |
- em [5], o projeto carregado tem o nome [RdvMedecins-SqlServer-01];
- em [6], alteramos o seu nome para [RdvMedecins-Oracle-01]
![]() |
- em [7], adicionamos outro projeto à solução. Este é retirado da pasta [RdvMedecins-SqlServer-01] do projeto que removemos anteriormente da solução;
- em [8], o projeto [RdvMedecins-SqlServer-01] foi novamente adicionado à solução.
O projeto [RdvMedecins-Oracle-01] é idêntico ao projeto [RdvMedecins-SqlServer-01]. Precisamos de fazer algumas alterações. Em [App.config], iremos modificar a cadeia de ligação e o [DbProviderFactory], que devem ser adaptados a cada SGBD.
<!-- connection chain on base -->
<connectionStrings>
<add name="monContexte" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)));User Id=RDVMEDECINS-EF;Password=rdvmedecins;" providerName="Oracle.DataAccess.Client" />
</connectionStrings>
<!-- the factory provider -->
<system.data>
<DbProviderFactories>
<remove invariant="Oracle.DataAccess.Client" />
<add name="Oracle Data Provider for .NET" invariant="Oracle.DataAccess.Client" description="Oracle Data Provider for .NET" type="Oracle.DataAccess.Client.OracleClientFactory, Oracle.DataAccess, Version=4.112.3.0, Culture=neutral, PublicKeyToken=89b483f429c47342" />
</DbProviderFactories>
</system.data>
- linha 3: o nome de utilizador e a palavra-passe;
- linhas 6–11: o DbProviderFactory. A linha 9 faz referência a uma DLL [Oracle.DataAccess] que não temos. Podemos obtê-la usando o NuGet [1]:
![]() |
- em [2], digite a palavra-chave «oracle» na caixa de pesquisa;
- em [3], selecione o pacote apropriado [Oracle Data Provider]. Este é o conector ADO.NET da Oracle;
![]() |
- em [4], a referência é adicionada;
- em [5], em [App.config], deve especificar a versão correta da DLL. Pode encontrá-la nas suas propriedades.
No ficheiro [Entities.cs], deve adaptar o esquema das tabelas que serão geradas. O esquema utilizado é o nome do utilizador proprietário das tabelas.
[Table("MEDECINS", Schema = "RDVMEDECINS-EF")]
public class Medecin : Personne
{...}
[Table("CLIENTS", Schema = "RDVMEDECINS-EF")]
public class Client : Personne
{...}
[Table("RVS", Schema = "RDVMEDECINS-EF")]
public class Rv
{...}
[Table("CRENEAUX", Schema = "RDVMEDECINS-EF")]
public class Creneau
{...}
Configuramos a compilação do projeto:
![]() |
- em [1], atribuímos um nome diferente ao assembly que será gerado;
- em [2], especificamos também um namespace padrão diferente;
- em [3], especificamos 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:
Recordamos ter encontrado o mesmo erro com o MySQL. Isto está relacionado com o tipo do campo Timestamp nas entidades. Fazemos a mesma modificação. Nas entidades, substituímos as três linhas
[Column("TIMESTAMP")]
[Timestamp]
public byte[] Timestamp { get; set; }
com o seguinte:
[ConcurrencyCheck]
[Column("VERSIONING")]
public int? Versioning { get; set; }
Por isso, vamos alterar o tipo da coluna de byte[] para int?. Recorde-se que, tanto no SQL Server como no MySQL, a coluna da tabela utilizada para gerir a concorrência de acesso recebia um valor atribuído pelo SGBD sempre que uma linha era inserida ou modificada. A partir de agora, utilizaremos um campo de entidade do tipo inteiro. No SGBD, utilizaremos procedimentos armazenados para incrementar esse inteiro em um sempre que uma linha for inserida ou modificada.
Efetuaremos a alteração acima em todas as quatro entidades e, em seguida, reexecutaremos a aplicação. Obteremos então o seguinte erro:
A linha 1 indica que o conector Oracle ADO.NET não consegue eliminar a base de dados existente. Vamos analisar o que está a acontecer. O código em [CreateDB_01.cs] é o seguinte:
using System;
using System.Data.Entity;
using RdvMedecins.Models;
namespace RdvMedecins_01
{
class CreateDB_01
{
static void Main(string[] args)
{
// create the database
Database.SetInitializer(new RdvMedecinsInitializer());
using (var context = new RdvMedecinsContext())
{
context.Database.Initialize(false);
}
}
}
}
A linha 15 aciona a execução da classe [RdvMedecinsInitializer] (linha 12). Esta classe é a seguinte:
public class RdvMedecinsInitializer : DropCreateDatabaseAlways<RdvMedecinsContext>
Deriva da classe [DropCreateDatabaseAlways], que tenta eliminar e, em seguida, recriar a base de dados. Alteramos a definição da classe para:
public class RdvMedecinsInitializer : CreateDatabaseIfNotExists<RdvMedecinsContext>
A base de dados é criada apenas se não existir. Executamos novamente [CreateDB_01.cs] e já não há erros. Mas no [EMS Manager], vemos que a base de dados [RDVMEDECINS-EF] permanece vazia. Como o EF 5 encontrou uma base de dados existente, não fez nada. Só entra em ação se a base de dados não existir. A partir daí, ficamos presos num ciclo vicioso. Na verdade, a cadeia de ligação ao SGBD é a seguinte:
<connectionStrings>
<add name="monContexte" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)));User Id=RDVMEDECINS-EF;Password=rdvmedecins;" providerName="Oracle.DataAccess.Client" />
</connectionStrings>
Linha 2: A cadeia de ligação utiliza um nome de utilizador em vez de um nome de base de dados. Este utilizador deve existir.
Devemos, portanto, criar manualmente a base de dados [RDVMEDECINS-EF] utilizando a ferramenta [EMS Manager for Oracle]. Não descreveremos todos os passos, mas apenas os mais importantes.
A base de dados Oracle terá a seguinte configuração:
As tabelas
![]() |
As várias tabelas têm as chaves primárias e estrangeiras que estas mesmas tabelas tinham nos dois exemplos anteriores. As chaves estrangeiras têm, especificamente, o atributo ON DELETE CASCADE.
Sequências
Criámos aqui sequências Oracle. Estas são geradores de números consecutivos. Existem 5 delas [1].
![]() |
- Em [2], vemos as propriedades da sequência [SEQUENCE_CLIENTS]. Ela gera números consecutivos em incrementos de 1, começando em 1 até um valor muito grande.
Todas as sequências são construídas com base no mesmo modelo.
- [SEQUENCE_CLIENTS] será utilizada para gerar a chave primária da tabela [CLIENTS];
- [SEQUENCE_DOCTORS] será utilizada para gerar a chave primária da tabela [DOCTORS];
- [SEQUENCE_SLOTS] será utilizada para gerar a chave primária da tabela [SLOTS];
- [SEQUENCE_RVS] será utilizada para gerar a chave primária da tabela [RVS];
- [SEQUENCE_VERSIONS] será utilizado para gerar os valores das colunas [VERSIONING] em todas as tabelas.
Gatilhos
Um gatilho é um procedimento executado pelo SGBD antes ou depois de um evento (Insert, Update, Delete) numa tabela. Temos 8 deles [1]:
![]() |
Vejamos o código DDL do gatilho [TRIGGER_PK_CLIENTS], que preenche a chave primária da tabela [CLIENTS]:
- Linhas 1-5: Antes de cada operação INSERT na tabela [CLIENTS];
- linha 6: a coluna [ID] assumirá o valor seguinte da sequência [SEQUENCE_CLIENTS]. A chave primária terá, assim, valores consecutivos fornecidos pela sequência.
Os gatilhos [TRIGGER_PK_MEDECINS, TRIGGER_PK_CRENEAUX, TRIGGER_PK_RVS] funcionam de forma semelhante.
Vejamos o código DDL do gatilho [TRIGGER_VERSIONS_CLIENTS], que preenche a coluna [VERSIONING] da tabela [CLIENTS]:
- Linhas 1-2: Antes de cada operação INSERT ou UPDATE na tabela [CLIENTS];
- linha 8: a coluna [VERSIONING] assumirá o valor seguinte da sequência [SEQUENCE_VERSIONS]. A coluna [VERSIONING] terá, assim, valores consecutivos fornecidos pela sequência.
Os gatilhos [TRIGGER_VERSION_MEDECINS, TRIGGER_VERSION_CRENEAUX, TRIGGER_VERSION_RVS] funcionam de forma semelhante. As quatro colunas [VERSIONING] obtêm os seus valores a partir da mesma sequência.
O script para gerar as tabelas da base de dados Oracle [RDVMEDECINS-EF] foi colocado na pasta [RdvMedecins / databases / oracle]. O leitor pode carregá-lo e executá-lo para criar as tabelas.
Depois de feito isto, os vários programas do projeto podem ser executados. Produzem os mesmos resultados que com o SQL Server, exceto o programa [ModifyDetachedEntities], que falha pela mesma razão que falhou com o MySQL. O problema é resolvido da mesma forma. Basta copiar o programa [ModifyDetachedEntities] do projeto [RdvMedecins-MySQL-01] para o projeto [RdvMedecins-Oracle-01]. Isto apresenta então um novo problema:
- linhas 1–4: o cliente desligado foi atualizado com sucesso;
- linha 6: uma exceção conhecida. Esta é a que ocorre quando se tenta modificar uma entidade sem ter a versão correta. No entanto, neste caso, não pretendíamos modificar, mas sim eliminar a entidade:
// remove out-of-context entity
using (var context = new RdvMedecinsContext())
{
// here we have a new empty context
// put client1 in the context in a deleted state
context.Entry(client1).State = EntityState.Deleted;
// save the context
context.SaveChanges();
}
O EF 5 recusou-se a eliminar o client1 da base de dados porque o client1 (linha 6) não tinha a mesma versão. Não tínhamos encontrado este problema com o MySQL. Estamos gradualmente a perceber que os conectores ADO.NET para diferentes SGBDs apresentam ligeiras diferenças. Corrigimos isso da seguinte forma:
using (var context = new RdvMedecinsContext())
{
// here we have a new empty context
// put client1 in the context to delete it
context.Clients.Remove(context.Clients.Find(client1.Id));
// save the context
context.SaveChanges();
}
e funciona.
5.3. Arquitetura multicamadas baseada no EF 5
Voltemos ao nosso estudo de caso descrito no parágrafo 2 da página 7.
![]() |
Começaremos por criar a camada de acesso aos dados [DAO]. Para tal, duplicamos o projeto de consola do VS 2012 [RdvMedecins-SqlServer-02] para [RdvMedecins-Oracle-02] [1]:
![]() |
- em [2], eliminamos o projeto [RdvMedecins-SqlServer-02];
![]() |
- em [3], adicionamos um projeto existente à solução. Retirámo-lo da pasta [RdvMedecins-Oracle-02] que acabámos de criar;
- em [4], o novo projeto tem o mesmo nome que o que foi eliminado. Vamos alterar o seu nome;
![]() |
- Em [5], alterámos o nome do projeto;
- Em [6], modificamos algumas das suas propriedades, como o nome do conjunto;
- em [7], a pasta [Models] é eliminada e substituída pela pasta [Models] do projeto [RdvMedecins-Oracle-01]. Isto deve-se ao facto de os dois projetos partilharem os mesmos modelos.
![]() |
- em [8], as referências atuais do projeto;
- em [9], o conector Oracle ADO.NET foi adicionado utilizando a ferramenta NuGet.
No ficheiro [App.config], substitua as informações da base de dados SQL Server pelas da base de dados Oracle. Estas informações podem ser encontradas no ficheiro [App.config] do projeto [RdvMedecins-Oracle-01]:
<!-- connection chain on base -->
<connectionStrings>
<add name="monContexte" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)));User Id=RDVMEDECINS-EF;Password=rdvmedecins;" providerName="Oracle.DataAccess.Client" />
</connectionStrings>
<!-- the factory provider -->
<system.data>
<DbProviderFactories>
<remove invariant="Oracle.DataAccess.Client" />
<add name="Oracle Data Provider for .NET" invariant="Oracle.DataAccess.Client" description="Oracle Data Provider for .NET" type="Oracle.DataAccess.Client.OracleClientFactory, Oracle.DataAccess, Version=4.112.3.0, Culture=neutral, PublicKeyToken=89b483f429c47342" />
</DbProviderFactories>
</system.data>
Os objetos geridos pelo Spring também mudam. Atualmente, temos:
<!-- spring configuration -->
<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-Oracle-02].
Feito isso, estamos prontos para executar o teste da camada [DAO]. Primeiro, temos de garantir que a base de dados está preenchida (utilizando o programa [Fill] do projeto [RdvMedecins-Oracle-01]). O programa de teste é executado com sucesso.
Criamos a DLL do projeto tal como fizemos para o projeto [RdvMedecins-SqlServer-02] e reunimos todas as DLLs do projeto numa pasta [lib] criada dentro de [RdvMedecins-Oracle-02]. Estas serão as referências para o projeto web [RdvMedecins-Oracle-03] que se segue.
![]() |
Estamos agora prontos para criar a camada [ASP.NET] da nossa aplicação:
![]() |
Começaremos pelo projeto [RdvMedecins-SqlServer-03]. Duplicamos a pasta deste projeto para [RdvMedecins-Oracle-03] [1]:
![]() |
- em [2], utilizando o VS 2012 Express for the Web, abrimos a solução na pasta [RdvMedecins-Oracle-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 às DLLs que acabámos de guardar numa pasta [lib] dentro do projeto [RdvMedecins-Oracle-02].
Resta apenas modificar o ficheiro [Web.config]. Substituímos o seu conteúdo atual pelo conteúdo do ficheiro [App.config] do projeto [RdvMedecins-Oracle-02]. Depois de feito isto, executamos o projeto web. Funciona. Não devemos esquecer-nos de preencher a base de dados antes de executar a aplicação web.




























