quinta-feira, 10 de novembro de 2011

Receita de bolo para EF CODE FIRST e ORACLE 10G

Utilizando Entity Framework no desenvolvimento de aplicações, temos a vantagem de trabalhar de forma independeente com qualquer banco de dados, desde que haja um "provider" específico para Entity Framework.

Neste post, mostro que é possível utilizar EF com o banco de dados Oracle. Existem diversos providers de Oracle para o EF, um bom exemplo é o da DevArt (dotConnect), porém é uma solução paga. A própria Oracle está desenvolvendo um provedor com suporte ao Entity Framework, porém até o momento deste POST ainda se encontra na versão Beta 3, porém em breve teremos a versão de produção, aí então é só atualizar.


Pré-requisitos:

* ORACLE 10G express edition previamente instalado
http://www.oracle.com/technetwork/database/express-edition/database10gxe-459378.html

* ODAC1120250Beta_EntityFramework_32bit_xcopy
http://www.oracle.com/technetwork/developer-tools/visual-studio/downloads/index.html

* EntityFramework Code First 4.1
http://www.microsoft.com/download/en/details.aspx?id=26825

Mãos à massa!

Crie um novo usuário no Oracle, para este post criei um usuário chamado REPOSITORIO. O banco de dados para este exemplo consite em uma tabela de PESSOAS, que possui zero ou mais telefones, relacionada com uma tabela de TELEONES A estrutura do banco de dados ficou assim:

CREATE TABLE "PESSOAS"
( "ID" NUMBER,
"NOME" VARCHAR2(50),
CONSTRAINT "PESSOAS_PK" PRIMARY KEY ("ID") ENABLE
)
/
CREATE TABLE "TELEFONES"
( "ID" NUMBER NOT NULL ENABLE,
"PESSOA_ID" NUMBER NOT NULL ENABLE,
"NUMERO" VARCHAR2(10) NOT NULL ENABLE,
CONSTRAINT "TELEFONES_PK" PRIMARY KEY ("ID") ENABLE,
CONSTRAINT "TELEFONES_FK" FOREIGN KEY ("PESSOA_ID")
REFERENCES "PESSOAS" ("ID") ENABLE
)
/
Rem Nenhum function encontrado para gerar a DDL.
CREATE UNIQUE INDEX "PESSOAS_PK" ON "PESSOAS" ("ID")
/
CREATE UNIQUE INDEX "TELEFONES_PK" ON "TELEFONES" ("ID")
/
Rem Nenhum procedure encontrado para gerar a DDL.
CREATE SEQUENCE "PESSOAS_SEQ" MINVALUE 1 MAXVALUE 999999999999999999999999999 INCREMENT BY 1 START WITH 21 CACHE 20 NOORDER NOCYCLE
/
CREATE SEQUENCE "TELEFONES_SEQ" MINVALUE 1 MAXVALUE 999999999999999999999999999 INCREMENT BY 1 START WITH 21 CACHE 20 NOORDER NOCYCLE
/
CREATE OR REPLACE TRIGGER "BI_PESSOAS"
before insert on "PESSOAS"
for each row
begin
select "PESSOAS_SEQ".nextval into :NEW.ID from dual;
end;
/
ALTER TRIGGER "BI_PESSOAS" ENABLE
/
CREATE OR REPLACE TRIGGER "BI_TELEFONES"
before insert on "TELEFONES"
for each row
begin
select "TELEFONES_SEQ".nextval into :NEW.ID from dual;
end;
/
ALTER TRIGGER "BI_TELEFONES" ENABLE
/
view raw repositorio.sql hosted with ❤ by GitHub


Crie uma nova aplicação Console e adicione as seguintes referencias:

* EntityFramework.dll
* Oracle.DataAccess.dll (na pasta de instalação do Client do Oracle)

Veja o código completo da aplicação Console (coloquei todas as classes no mesmo arquivo Program.cs para este post, obviamente costumo separar um arquivo para cada classe nos meus projetos) :

//POST: http://silverlightrush.blogspot.com/2011/11/receita-de-bolo-para-ef-code-first-e.html
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using Oracle.DataAccess.Client;
namespace ConsoleApplication3
{
/// <summary>
/// Classe que retorna a conexão com o banco de dados
/// </summary>
public class OracleConnectionFactory : IDbConnectionFactory
{
public DbConnection CreateConnection(string nameOrConnectionString)
{
return new OracleConnection(nameOrConnectionString);
}
}
/// <summary>
/// Classe mapeada para a tabela de Pessoas
/// </summary>
[Table("PESSOAS", Schema = "REPOSITORIO")]
public class Pessoa
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), Column("ID")]
public decimal Id { get; set; }
[Column("NOME"), StringLength(50)]
public string Nome { get; set; }
public virtual ICollection<Telefone> Telefones { get; set; }
public Pessoa()
{
Telefones = new HashSet<Telefone>();
}
}
/// <summary>
/// Classe mapeada para a tabela de Telefones
/// </summary>
[Table("TELEFONES", Schema = "REPOSITORIO")]
public class Telefone
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), Column("ID")]
public decimal Id { get; set; }
[Required, Column("PESSOA_ID")]
public decimal? PessoaId { get; set; }
[ForeignKey("PessoaId")]
public virtual Pessoa Pessoa { get; set; }
[StringLength(10), Required, Column("NUMERO")]
public string Numero { get; set; }
}
/// <summary>
/// Classe que representa o Contexto a ser utilizado para acesso ao banco
/// </summary>
public class MyDbContext : DbContext
{
private const string ConnectionString
= "DATA SOURCE=SERVER_ORACLE; User Id=REPOSITORIO; PASSWORD=repositorio; Persist Security Info=true";
static MyDbContext()
{
Database.DefaultConnectionFactory = new OracleConnectionFactory();
}
public MyDbContext()
: base(ConnectionString)
{
}
public DbSet<Pessoa> Pessoas { get; set; }
public DbSet<Telefone> Telefones { get; set; }
}
/// <summary>
/// Código para a aplicação Console
/// </summary>
class Program
{
static void Main(string[] args)
{
using (var db = new MyDbContext())
{
//adicionar uma pessoa
var pessoa = new Pessoa { Nome = "Jone Polvora" };
db.Pessoas.Add(pessoa);
db.SaveChanges();
//adicionar um telefone para a pessoa
var telefone = new Telefone { Numero = "6712345678", Pessoa = pessoa };
db.Telefones.Add(telefone);
db.SaveChanges();
//imprime a lista de pessoas
foreach (var p in db.Pessoas.ToList())
{
Console.WriteLine(p.Nome);
//imprime os telefones da pessoa atual
foreach (var t in p.Telefones)
{
Console.WriteLine(t.Numero);
}
}
//alterar a pessoa
pessoa.Nome = "Outro nome";
db.SaveChanges();
//remover o telefone da pessoa
db.Telefones.Remove(telefone);
db.SaveChanges();
//remover a pessoa
db.Pessoas.Remove(pessoa);
db.SaveChanges();
Console.WriteLine("Pressione Delete para remover todos os registros");
var key = Console.ReadKey();
if (key.Key == ConsoleKey.Delete)
{
db.Telefones.OrderBy(t => t.Id).ToList().ForEach(t => db.Telefones.Remove(t));
db.Pessoas.OrderBy(p => p.Id).ToList().ForEach(p => db.Pessoas.Remove(p));
db.SaveChanges();
}
}
}
}
}
view raw Program.cs hosted with ❤ by GitHub
Não esquecer de corrigir a string de conexão com o valor correto (geralmente é o nome da estação local).

Nenhum comentário:

Postar um comentário