本文的上下文环境
操作系统:Win7 x64 Ultimate
开发工具:Visual Studio 2013
一、前言
在以前开发的大多数场景下,使用的开发语言是C#,使用的数据库是Sql Server,这样的组合,必然少不了Entity Framework这样的ORM,自从习惯了Entity Framework,就甩不掉了。这次因为一些历史原因,数据库必须用Oracle,其实,我不太喜欢Oracle,这样的数据库给我的印象是使用起来,无论是安装客户端,配置连接,都太费劲了。
有些事情,你还真得硬着头皮去尝试,你不去做,永远都不会明白。因为我之前的项目Entity Framewor +Sql Server是比较常用的,所以对Entity Framework这样的ORM已经很熟悉了,Entity Framework的DataBase First和Model First,使用的时候,总会遇到一些问题,比如表字段的维护,只能通过设计器,更新,如果表很多的话,设计器显示很慢,更新还经常出错,所以我一直都是用Code First。这次既然是Oracle,我以为很简单,换个引用或连接串就行了(如果以后东西真的这样就好了)。
二、ODP.Net安装
首先搜索一番,知道有个ODP.Net,官方介绍:Oracle Data Provider for .NET (ODP.NET) features optimized ADO.NET data access to the Oracle database. ODP.NET allows developers to take advantage of advanced Oracle database functionality, including Real Application Clusters, XML DB, and advanced security. The data provider can be used with the latest .NET Framework 4.5.1 version。这个ODP.Net其实和微软的System.Data.OracleClient一样,都是提供对Oracle数据库访问的驱动,不过ODP.Net是Oracle自己开发的,可能有些人会以为,项目中换下引用就行了,其实一开始我也这么认为,之后查了些资料才明白,System.Data.OracleClient用的是Oracle的“最小驱动”,使用的时候应该是不需要安装Oracle客户端的,本人没有实践过,使用过的朋友可以帮忙证明下。之后看了System.Data.OracleClient Namespace的介绍:The System.Data.OracleClient namespace is the .NET Framework Data Provider for Oracle.This types in System.Data.OracleClient are deprecated and will be removed in a future version of the .NET Framework. For more information, see Oracle and ADO.NET.这个我看到最重要的是System.Data.OracleClient已deprecated,在将来的 .Net Framework里就看不到了。所以,现在最好是用ODP.Net。
首先要下载ODP.Net,这个要和Oracle服务器端的版本相对应,我用的是Oracle 11g,所以下载了ODAC1120320Xcopy_32bit,我的操作系统是x64的,可以下载32位和64位的,一开始下载了64位的,但是在IIS 7.5里运行网站的时候,提示”Could not load file or assembly ‘Oracle.DataAccess‘ or one of its dependencies. An attempt was made to load a program with an incorrect format.",所以我下载了32位的,如果IIS 7.5里的Advanced Setting下Enable 32-Bit Applications设为True,就能正常运行了,64位下如果Enable 32-Bit Applications设为False估计也行,对于ODP.Net的安装,这里面有详细的介绍,正确安装相当重要。
Oracle的这部分完成了,下面就是ORM的选择了,解压ODP.Net后,在odp.net4\odp.net\doc里看到一个readme.htm,打开仔细阅读,看到这条“ODP.NET 11.2.0.3 does not support Code First nor the DbContext APIs.“瞬间就被震住了。之后在Oracle Community看到社区管理员的回答ODP.NET does not support EF Code First yet. For the next ODP.NET release (ODAC 12c Release 3), Oracle plans to support this feature. The plan is to release sometime in 2014.我只感到这大Oracle的反应真够慢的。所以,只能放弃Entity Framework了,听朋友推荐说可以用Fluent NHibernate。
三、Fluent NHibernate配置
所以决定try,在VS2013里引用FluentNHibernate,Install-Package FluentNHibernate -Version 1.4.0,目前是最新版。接下来就是基础搭建了。
由于在继承ClassMap的那种方式下,一个数据库表会写2个实体,感觉太麻烦了,我这里用的是AutoMapping,实体和数据库表一一对应,没有考虑多表关联问题。
首先是个DBContext:
namespace Test.DataAccess.FNhibernate { public class DBContext { private static ISessionFactory _sessionFactory; private static ISessionFactory SessionFactory { get { if (_sessionFactory == null) { InitializeSessionFactory(); } return _sessionFactory; } } private static void InitializeSessionFactory() { string connectionString = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=10.1.1.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orcl)));User Id=test;Password=test123;"; var cfg = new DBConfiguration(); _sessionFactory = Fluently.Configure().Database( OracleClientConfiguration.Oracle10 .ConnectionString(connectionString) .Provider<NHibernate.Connection.DriverConnectionProvider>() .Driver<NHibernate.Driver.OracleClientDriver>() ) .Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<PRODUCT>(cfg).Conventions.Setup(c => { c.Add<PrimaryKeyConvention>(); c.Add<CascadeConvention>(); }))) .BuildSessionFactory(); } public static ISession OpenSession() { return SessionFactory.OpenSession(); } /// <summary> /// 这个暂时用,可能重复 /// </summary> /// <returns></returns> public static long GenerateID() { long i = 1; foreach (byte b in Guid.NewGuid().ToByteArray()) { i *= ((int)b + 1); } string number = String.Format("{0:d9}", (DateTime.Now.Ticks / 10) % 1000000000); return long.Parse(number); } } }
接下来是一张和数据库表Product相对应的实体:
namespace Test.DataAccess.FNhibernate.DB { public class PRODUCT { public virtual int PRODUCT_ID { get; set; } public virtual string PRODUCT_NAME { get; set; } } }
接下来是映射关系:
namespace Test.DataAccess.FNhibernate { public class DBConfiguration : DefaultAutomappingConfiguration { public override bool ShouldMap(Type type) { return type.Namespace == "TEST.DataAccess.FNhibernate.DB"; } public override bool IsId(Member member) { string className = member.DeclaringType.Name; if (member.Name == className + "_ID") { return true; } return false; } } }
指定主键的生成方式:(这里用的是自己手动赋值,就是在保存的时候,调用DBContext里的GenerateID()方法)
namespace HeLi.DataAccess.FNhibernate { public class PrimaryKeyConvention : IIdConvention { public void Apply(IIdentityInstance instance) { var type = instance.EntityType; if (type.Name == typeof(PRODUCT).Name) { instance.GeneratedBy.Assigned(); } } } }
四、测试
using (var session = DBContext.OpenSession()) { using (var tran = session.BeginTransaction()) { var list = session.CreateCriteria(typeof(PRODUCT)).List<PRODUCT>();//取出整个表 var dbUser = session.QueryOver<PRODUCT>().Where(p => p.PORUDCTID = 1).SingleOrDefault(); if (dbUser != null) { dbUser.PRODUCT_NAME = "镜子"; session.SaveOrUpdate(dbUser); } var product = new PRODUCT(); product.PRODUCT_ID = DBContext.GenerateID(); product.PRODUCT_NAME = "盒子"; session.Save(product); tran.Commit(); } }
ps:由于是事后之作,没有来得及整理demo,没有完整代码下载,Hope it can help you.
引用阅读:
http://www.oracle.com/technetwork/topics/dotnet/index-085163.html
http://dba.stackexchange.com/questions/44470/why-oracle-sql-developer-does-not-need-oracle-client
http://msdn.microsoft.com/en-us/library/system.data.oracleclient(v=vs.110).aspx
http://www.thebestcsharpprogrammerintheworld.com/blogs/connect-to-an-oracle-database-without-an-oracle-client.aspx http://www.oracle.com/technetwork/database/windows/downloads/utilsoft-087491.html