Oracle just released its newest version of Oracle Data Access Components (ODAC) that incudes support for Entity Framework 4.0. You can read more about this release herehttp://www.oracle.com/technetwork/topics/dotnet/whatsnew/index.html. You can also read this article about usage of ODP (Oracle Data Provider) with Entity Framework modelshttp://www.oracle.com/technetwork/issue-archive/2011/11-sep/o51odt-453447.html
One important note is that Code First is not officially supported in this release, and will be supported in a future release. Having said that, I wanted to see if I can use it, since Code First builds on top of EF 4, which is supported by this release. I was able to confirm that I can indeed access Oracle data and update it.
Here are step-by-step instructions.
You will need one prerequisite – Oracle engine itself. I used 11g Express edition, which is free for developer. You can download it from this page
http://www.oracle.com/technetwork/database/express-edition/downloads/index.html
http://www.oracle.com/technetwork/database/express-edition/downloads/index.html
Event those it is not required, I also installed Sql Developer, which is akin to SQL Server Management Studio, well mostly anyway. You can download it from this page
http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/index.html
http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/index.html
Once that is done, download and install ODAC from here
http://www.oracle.com/technetwork/topics/dotnet/whatsnew/index.html
http://www.oracle.com/technetwork/topics/dotnet/whatsnew/index.html
At this point, I ran SQL Developer, connected to my instance and looked at the existing database. You have to create a new connection for that, using File – New..->Database Connection menu. Under Other Users node I found HR, which contains a sample HR database.
Personally, I reset password for HR user to something that I know. This way I do not have to use SYS login.
Now that those tasks are out of the way, you are ready to get started. I assume you already have VS 2010 installed.
Now, start new project (I used Console application) and install Entity Framework Code First package reference into this project. You can use NuGet for that, which is what I did. Also add a reference to Oracle ODP assembly – Oracle.DataAccess. Then, I create a POCO class to match Countries table and setup DbContext. I use fluent API to configure that table. I just do it in the context class instead of creating a separate configuring class. Here is my country class.
public class Country
{
public string CountryID { get; set; }
public string CountryName { get; set; }
public decimal RegionID { get; set; }
}
And here is DbContext class:
public class Context : DbContext
{
public Context()
:base(new OracleConnection(ConfigurationManager.ConnectionStrings["OracleHR"].ConnectionString), true)
{
}
public DbSet<Country> Countries { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
modelBuilder.Entity<Country>().Property(p => p.CountryID).HasColumnName("COUNTRY_ID");
modelBuilder.Entity<Country>().HasKey(p => p.CountryID);
modelBuilder.Entity<Country>().Property(p => p.CountryName).HasColumnName("COUNTRY_NAME");
modelBuilder.Entity<Country>().Property(p => p.RegionID).HasColumnName("REGION_ID");
modelBuilder.Entity<Country>().ToTable("COUNTRIES", "HR");
}
}
Now, you have to setup connection string in app.config:
<?xml version="1.0"?> <configuration> <connectionStrings> <add name="OracleHR" connectionString="DATA SOURCE=localhost:1521/XE;PASSWORD=****;PERSIST SECURITY INFO=True;USER ID=HR;" providerName="Oracle.DataAccess.Client" /> </connectionStrings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" /> </startup> </configuration>
Here is the code that gets data from the table and inserts a new row.
using (var ctx = new Context())
{
var data = ctx.Countries.ToList();
ctx.Countries.Add(new Country() { CountryID = "CL", CountryName = "Chile", RegionID = 2 });
ctx.SaveChanges();
}
{
var data = ctx.Countries.ToList();
ctx.Countries.Add(new Country() { CountryID = "CL", CountryName = "Chile", RegionID = 2 });
ctx.SaveChanges();
}
Here are a few important points.
- Make sure to put correct password into connection string.
- I configure column names explicitly to match the database table.
- I had to use correct .NET types to match the Oracle data types. Here is MSDN article that describes this mapping http://msdn.microsoft.com/en-us/library/yk72thhd.aspx
- I am manually creating Oracle Connection and passing it into constructor of my context class. I tried to avoid that, but I kept getting exceptions. This approach worked perfectly.
In summary, one apparently can use newest Oracle provider to use with Code First. This approach is not officially supported by Oracle, at least not yet. So, use it at your own risk. You definitely cannot create new database as you can in SQL Server, so you have to maintain database separately. From my research, only DevArt provides full Code First support for Oracle with their own provider, at least according to their product page http://www.devart.com/dotconnect/entityframework.html
You can download my sample solution http://DotNetSpeak.com/Downloads/EFOracle.zip
[Update 2/12/2012] – this scenario is not officially supported by Oracle, so you should not use this in production.
Thanks, and feedback is appreciated.
Could you send me an example when the Country has a “Active” Boolean Property, please? The Column type is NUMBER(1,0) in the database, but I don’t know how can I implement the conversion NUMBER to Boolean?
var data = ctx.Countries.Where(c=>c.IsActive == 1).ToList(); // where 1 would be your constant
public class Country
{
public string CountryID { get; set; }
public string CountryName { get; set; }
public decimal RegionID { get; set; }
public byte IsActive { get; set; }
}
Thanks.
{
public int Id { get; set; }
public string Username { get; set; }
public int? JobtitleId { get; set; }
public virtual Jobtitle Jobtitle {get; set;}
}
{
public int Id { get; set; }
public string JobtitleName { get; set; }
}
{
modelBuilder.Conventions.Remove();
modelBuilder.Entity().HasKey(c => c.Id);
modelBuilder.Entity().Property(c => c.Id).HasColumnName(“ID”);
modelBuilder.Entity().Property(c => c.Username).HasColumnName(“USERNAME”);
modelBuilder.Entity().Property(c => c.JobtitleId).HasColumnName(“JOBTITLEID”);
modelBuilder.Entity().HasKey(j => j.Id);
modelBuilder.Entity().Property(j => j.Id).HasColumnName(“ID”);
modelBuilder.Entity().Property(j => j.JobtitleName).HasColumnName(“JOBTITLENAME”);
}
_db.SaveChanges();
Some customer has Jobtitle and some hasn’t. Can you help me?
Thanks.
Could you please inlcude script for those two tables?
Thanks.
I changed the int ID to decimal, and add ‘HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)’ string to modelBuilder and it works fine.
Can you tell me if it’s possible to have an MVC web application using Oracle database and the DATABASE FIRST approach : generating my data model (.edmx) from an existing oracle database (700+ tables!) ??? how can I generate my data model?
Although code first works, I would be very careful when it comes to using something in production that is not officially supported by Oracle. You can certainly reverse engineer the model by creating an edmx from your database, then using DbContext template to create Code First model. You simply right click on the mode and select Add Code Generation Item, then pick DbContext template. If you do not see that option, just download the appropriate template from VS Gallery, but I think VS 2010 ships with that one.
Nice example.
But is code first now already fully supported by the beta ODP driver?
Thanks in advance.
Gertjan Smit
Is lazy and eager loading fully supported?
I have not tried, but I fully expect they are.
i use entity framework 5 . but get ‘No MigrationSqlGenerator found for provider ‘Oracle.DataAccess.Client’. Use the SetSqlGenerator method in the target migrations configuration class to register additional SQL generators.’
when i update database.
i installed the oracle odp latest edition and oracle client 10g.
Thanks.
Indeed Microsoft only ships SQL Server migrations provider. You can either write your own by implementing required interfaces or wait for Oracle to implement the same. Alternatively, you can use a third party product to maintain your database schema separately from EF classes.
I think you would need to execute SQL with your sequence code before you save and put the value into your primary key property. DbContext has method to Execute arbitrarily SQL you can use.
I don´t know why, but the context-constructor was able to establish the Connection, the naming convention – as I usually do it – failed …
I am not sure at this point, just not enough information. Make sure you have client installed that matches your web site platform – 32 vs 64 bit. Check your machine.config to ensure Oracle provider is there. Look for inner exception to get more clues. Doublecheck your connection string….
“Extent1″.”EmployeeId” AS “EmployeeId”,
“Extent1″.”DepartmentId” AS “DepartmentId”,
“Extent1″.”RelatedEntityFqdn” AS “RelatedEntityFqdn”,
“Extent1″.”EmailAddress” AS “EmailAddress”
FROM “schemaname”.”EMPLOYEE” “Extent1″
and the second thing is column names, in that too it is taking double quotes while it should not. so can you tell the exact cause of the issue? and what do i need to do so that entity framework generates proper query for oracle?
The description of the exception is as follows :
Error : Schema specified is not valid. Errors: (27,12) : error 2019: Member Mapping specified is not valid. The type ‘Edm.Boolean[Nullable=False,DefaultValue=]‘is not compatible with ‘OracleEFProvider.number[Nullable=False,DefaultValue=,Precision=38,Scale=0]‘
You can also opt to just use numbers in your model.
I am getting the following error while connecting to Oracle 11G Database with ASP.NET MVC4 and EF 5.