Oracle + EF5 疑难杂症
PDF 版
http://files.cnblogs.com/xling/Oracle.pdf
Oracle 环境准备
ODAC
ODAC 全称 Oracle Data Access Components
下载:
- ODP.NET (Oracle Data Provider) http://www.oracle.com/technetwork/database/windows/downloads/index-090165.html
- ODTwithODAC (ODAC With Oracle Developer Tools)
http://www.oracle.com/technetwork/topics/dotnet/utilsoft-086879.html
ODTWthODAC 是用于VS的开发工具.
安装
采用ODAC XCopy 版, 就不需要安装体积庞大的 Oracle Client 了.
- 将 ODAC 解压到一个固定的位置, 比如 C:\ODAC , 注意,这个文件夹用完后不能删除
- 以管理员身份打开一个 CMD
- Cd C:\ODAC\ODP.NET\Managed\X64
- 运行 configure.bat (不要图快,直接右键以管理员身份运行, 没用!)
- 运行成功后, 会注册相关DLL到GAC中.
- 打开 Machine.config (C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config)
可以看到加入了一个 setion oracle.manageddataaccess.client,
在 DbProviderFactories 下加入了 ODP.NET, Managed Driver
对应 section oracle.manageddataaccess.client 还有一个配置:
<oracle.manageddataaccess.client> <version number="4.121.1.0"> <settings> <setting name="tns_admin" value="c:\odac\odp.net\managed\x64\..\..\..\network\admin" /> </settings> </version> </oracle.manageddataaccess.client>
其中, setting tns_admin 所指的地址即 tnsnames.ora 的目录.
- 将 D:\ODAC\network\admin\sample\tnsnames.ora 考到上一层目录:
即 c:\ODAC\network\admin\
修改成 :
dev =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.3)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = ORCL)
)
)DEV 即别名, 不区分大小写
HOST 即 ORACLE 服务器地址
PORT 为指定的监听端口
SERVICE_NAME 即ORACLE 实例名
如果需要 32位的, 可以重复上面的步骤, 不过运行的是 X86目录下面的 configure.bat 而已.
ODTWithODAC 一路 NEXT 即可.
在 VS 中连接数据库, 即可看到多出一个 ODP.NET 托管驱动程序选项:
用TNS 测试连接:
注意: 这里的 Tnsnames.ora 的位置, 不是上面步骤中的位置. 如果文件不存在, 请在 COPY一个到 admin 目录下.
用 EZ 测试连接:
网站运行可能出现的错误:
无法读取配置节 Oracle.manageddataaccess.client 因为它缺少节声明
请检查你的应用程序池
A, 如果 "启用32位应用程序"为 True , 请改为 False , 因为只安装了 64 位的 ODAC
B, 也可以把 32 位的 ODAC 安装上.
Entity Framework
支持
当前EF6 不支持 Oracle, 而且只能是 Database First.
表名/字段名 大小写的问题
在Oracle 中建的表, 在更新模型的时候, 全是大写, 表字段也全是大写:
这个问题对于习惯于 SQLServer 的你来说, 感觉一定是吹鼻子瞪眼,抓狂的想办法把表名变小写.
有办法变小写, 不过,了解了因果之后, 你一定会想:大写就大写吧.
建表语句 中的 表名/列名 用双引号括起来:
CREATE TABLE BK."Test"
(
"ID" NUMBER NOT NULL,
"Name" NUMBER NOT NULL
)
生成的表, 可以看到 表名/列表都有小写的字母了.
但是:
必须用
SELECT * FROM "Test";
即双引号括起来表名, 大小写必须一致.
这样虽然在 EF 里有了驼峰式, 但是写SQL又成了一大挑战.
类型转换
SQLServer 中的类型到.NET中的类型, 基本上都有一个完美的映射, 但是 ORACLE 就不同了. 一堆 Decimal 不说, 连最基本的 bool 都没有原生的映射.
要想用 bool 类型, 需要在模型项目的 App.config 中加上一段:
<oracle.manageddataaccess.client>
<version number="*">
<edmMappings>
<edmMapping dataType="number">
<add name="bool" precision="1" />
<add name="byte" precision="2" />
<add name="int16" precision="5" />
</edmMapping>
</edmMappings>
</version>
</oracle.manageddataaccess.client>
即:
精度为 1 的 NUMBER 字段,在模型中为 bool 类型
精度为 2 的 NUMBER 字段在模型中为 byte 类型
模型属性设置
更新属性方面:
这个翻译真的很蛋疼. 意向中 这个是用来 更新 诸如 精度修改 等小细节上的, 不过, 我这里把精度从默认的 38 改为 1 , 实体类型还是没有改为 bool, 需要手动更改, 或者把表从模型中删除, 在添加进来.
生成时验证:
假如这个选项为 True, 在你编译的时候, 出现以下情况:
全部生成成功, 但是有错误. 模型兼容性错误. 这个错误不引响程序的正常运行, 就是看着有一大堆错误而已.
把这个属性改为 False , 即可以在编译的时候屏蔽这样的错误.
TT模板
模型模板
这个结构是将 DbContext 和 实体分成两个不同的DLL, 好处不言而喻.
Model1.tt 是从 XXX.DbContext 下剪切出来的.
Model1.Context.tt 只做了少许修改, 加入了 XXX.DbEntity 的引用, 变化不大.
Model1.tt 中加入了字段注释, Required / StringLength 等, 生成的效果如下:
namespace XXY.DbEntity
{
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json; /// <summary>
/// 承运人运价表
/// </summary>
[Serializable,DataContract(IsReference = true),JsonObject(IsReference = false)]
public partial class PRICE
{
public PRICE()
{
this.PRICE_DETAIL = new HashSet<PRICE_DETAIL>();
} /// <summary>
/// 价格流水号 必填项
/// </summary>
[DataMember , Required]
public decimal PRICE_ID { get; set; } /// <summary>
/// 承运人流水号 必填项
/// </summary>
[DataMember , Required]
public decimal CARRIER_ID { get; set; } /// <summary>
/// 承运人代码 必填项
/// </summary>
[DataMember , Required, StringLength()]
public string CARRIER_CODE { get; set; } /// <summary>
/// 承运人名称
/// </summary>
[DataMember, StringLength()]
public string CARRIER_NAME { get; set; }
StringLength 读取的是模型中的字段最大长度.
Required 是根据模型中的字段是否可为 Null
Summary 部分:
因为是 Database First, 原本跟据数据生成的模型, 并不会把数据库注释写到模型中, 我写了一个小工具, 后期把注释更新到模型中, 该工具在本文的最下方有提供下载.
DataContract IsReference = true 是为了在 WCF 中使用, 而不出现 "循环引用" 的错误.
JsonObject IsReference = false 是为了生成的 Json 不出现 $1 之类的东西.
在 Model1.tt 的第 6行:
const string inputFile = @"..\XXX.DbContext\Model1.edmx";
用于指定该模板依赖的 edmx 文件的位置.
SEQUENCE
Oracle 中没有自增长, 只有 SEQUENCE.
看网上有很多文章都是说新建一个触发器, 当插入数的时候, 取一个 SQUENCE 出来.
不是说触发器不好, 我不乐意使用触发器.
这里,我写了一个 TT模板, 从数据库中把所有的当前数据库的 SEQUENCE 名称取出来, 写入到一个枚举中.
用法其实就是执行一个SQL语句, 从指定SEQUENCE 中取下一个值.
SELECT XXX.NEXTVAL FROM DUAL
针对这个, 我做了扩展方法:
public static decimal GetNextVal(this System.Data.Entity.DbContext ctx, string seqName) {
return ctx.Database.SqlQuery<decimal>(string.Format("SELECT {0}.NEXTVAL FROM DUAL", seqName)).First();
} public static decimal GetNextVal<T>(this DbContext ctx, T enumValue) where T : struct, IComparable, IConvertible, IFormattable {
return ctx.GetNextVal(enumValue.ToString());
}
使用:
price.PRICE_ID = db.GetNextVal(Sequences.PRICES_SEQ);
在 Sequence.ttinclude 文件的 29/30 行
const string ConnectionString = @"Persist Security Info=True;Data Source=192.168.0.3;User ID=BK;password=bk;";
const string SQL = @"SELECT * FROM all_sequences WHERE SEQUENCE_OWNER = 'BK'";
ConnectionString 就不用说了, 改成自己的数据库连接字符串.
将 SQL 中的 SEQUENCE_OWNER 改成你的数据库登陆用户(该用户要能看到SEQUENCE)
SEQUENCE 模板 和 模型模板在本文的最下方会给出下载地址.
大小写敏感的问题
这里说的大小写每感,不同于上面说的表名/字段名大小写的问题.
ORACLE 中,默认的 字段的值 是 大小写敏感 的.
搜了一下ORACLE 的忽略大小写的方法, 大部分都是用 UPPER 或是更改会话设置:
alter session set NLS_COMP=LINGUISTIC;
alter session set NLS_SORT=BINARY_CI;
第一种对应到EF 里就是用 ToUpper, 但是这样一来或多或少的影响查询性能.
第二种不方便实现, 且这样改动可能会造成其它方面的问题.
用第一种办法,略显繁琐.
我改了一下 模型文件 的模板(Model1.Context.tt), 重载了 ShouldValidateEntity 方法, 在它里面做了一些手脚.
protected override bool ShouldValidateEntity(DbEntityEntry entityEntry) {
UpperConverter.Convert(entityEntry.Entity);
return base.ShouldValidateEntity(entityEntry);
} internal static partial class UpperConverter {
private static Dictionary<Type, List<PropertyInfo>> TPS = new Dictionary<Type, List<PropertyInfo>>();
public static void Add<T>(params Expression<Func<T, string>>[] exprs) { foreach (var expr in exprs) { PropertyInfo pi = null; switch (expr.Body.NodeType) {
case ExpressionType.MemberAccess:
pi = (PropertyInfo)((MemberExpression)expr.Body).Member;
break;
default:
throw new InvalidOperationException();
} if (TPS.ContainsKey(pi.DeclaringType))
TPS[pi.DeclaringType].Add(pi);
else {
TPS.Add(pi.DeclaringType, new List<PropertyInfo>() { pi });
}
}
} public static void Convert(object obj) {
var type = obj.GetType();
if (TPS.ContainsKey(type)) {
var ps = TPS[type];
foreach (var p in ps) {
var value = (string)p.GetValue(obj);
if (!string.IsNullOrWhiteSpace(value))
p.SetValue(obj, value.ToUpper());
}
}
}
} internal static partial class UpperConverter { public static void Set() { #region 审核权限
Add<BOOKING_ORDER_PERMISSIONS>(p => p.CARRIER_CODE, p => p.LOADING_PORT, p => p.ROUTE_CODE, p => p.SHIP_CODE,
p => p.VOYAGE);
Add<SPECIAL_PRICE_RULE>(p => p.CARRIER_CODE, p => p.ROUTE_CODE, p => p.SHIP_CODE, p => p.VOYAGE);
Add<SPECIAL_PRICE_RULE_CONTA>(p => p.SIZETYPE_CODE);
#endregion
…
这样在 db.SaveChange 的时候, 就会针对 Add<XXX>(…) 中列出的属性做大写转换.
这样一来, 就可以在 EF 中少写很多 ToUpper , 以优化查询性能.
事务报错
该部分原来已发过博文: http://www.cnblogs.com/xling/p/3900222.html
这里把它摘录过来:
错误:未能加载 Oracle.ManagedDataAccessDTC.dll 或它的依赖项
本地WIN7/8.1运行一点问题都没有。打包到 WIN 2008 上,解决了一堆环境问题后,一个大难题出现了:
Could not load file or assembly 'Oracle.ManagedDataAccessDTC.dll',什么 PSPManager..ctor 之类的
出现这个问题是因为某些地方用了 TransactionScope 。
把驱动卸掉,重装了N回,重启了N回,于事无补。
把这个DLL放到 Bin 下,运行网站直接就报错,还是无法加载。
Oracle 官方文档中只说不要直接引用这个DTC.dll ,会由 ManagedDataAccess 自动去调用,要区分 32位和64位,其它的基本没提。
GOOGLE上、BING上可以搜到几个相关的贴子,但是都是没有结果。度娘就更不用提了。
跟据报的那什么 PSPManager..Ctor 用反编译工具查看了一下,跟本就没有那个类。
不过有个 Microsoft.VisualC 的引用。
本地GAC (C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.VisualC)下有个11.0.xxx 版本的,
对照那台测试服务器,发现只有个8.XXX的版本。
尝试把本地的考过去,运行结果一样,没有用处。
眼看加班都3个半小时了,加上一下午时间,都整了快8个小时,还没整好这玩意,心里急的冒火。
顺手搜了一下C++运行库,下了个64位的
Microsoft Visual C++ 2010 SP1 Redistributable Package (x64)
http://www.microsoft.com/zh-cn/download/confirmation.aspx?id=13523
安装,重启网站,在测试,通过!
反向伴随类
由于是 Database First , 实体都是自动生成的, 任何手工修改都是无效的. 要想对某个实体的某个属性加个 DataAnnoation , 就需要写一个 Partial 出来.
我没有这样做, 我做了个反向的 伴随类 处理.
[AttributeUsage(AttributeTargets.Class)]
public class AnnoationForAttribute : Attribute {
public AnnoationForAttribute(Type type); public Type ForType {
get;
set;
}
} public class AnnorationHelper { public static void AutoMap() { var types = typeof(AnnorationHelper).Assembly.GetTypes();
foreach (var t in types) {
var attr = (AnnoationForAttribute)t.GetCustomAttributes(typeof(AnnoationForAttribute), false).FirstOrDefault();
if (attr != null)
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(attr.ForType, t), attr.ForType);
} } }
反向伴随类的声明:
[AnnoationFor(typeof(DbEntity.PRICE))]
public class PRICE { [CompareWith("EFFECTIVE_DATE", CompareWithOpts.Gt)]
public object EXPIRATION_DATE {
get;
set;
}
}
现在, 只需要在 Global 里调用:
AnnorationHelper.AutoMap();
即可把反向伴随类注册到实体类上.
OracleFunction ?
使用 SqlServer + EF , 可以用 SqlFunctions, 但是Oracle 并没有提供类似的功能.
Oracle 的函数到 LINQ 的函数映射可以参考:
http://docs.oracle.com/cd/E11882_01/win.112/e23174/canonical_map.htm#ODPNT7777
文档中说会把 concat 转化为 "xx"||"xx" 或 CONCAT 方法的调用, 但是在实际中, 却无法将 STRING 和 DECIMAL 用 String.Concat 连接起来.
为了一个特殊业务, 我在这个地方耗了好几个小时, 没有办法, 只能通过自定义函数解决了.
CREATE OR REPLACE function BK.ToString(P NUMBER)
return VARCHAR
is
V VARCHAR(10);
begin
select TO_CHAR(P) into V FROM DUAL;
return V;
end;
然后更新模型, 将函数添加到模型中.
定义一个 FUNCTION 的映射.
public static class OracleFunctions {
[EdmFunction("Model.Store", "TOSTRING")]
public static string ToString(decimal d) {
throw new InvalidOperationException("Not to be called from client code");
}
}
Model.Store 和模型的命名空间一样(未验证不一样是否可以)
然后 在LINQ TO SQL 中 用 拼接字符串的方法使用 :
let tmp = sp.PORT_CODE.ToUpper() + "," + OracleFunctions.ToString(s.COURSE)
很简单的一个功能, 搞的很蛋疼.
文中所述的下载地址:
http://files.cnblogs.com/xling/OracleEF.7z
捧个场, 推个贱。
Oracle + EF5 疑难杂症的更多相关文章
- [部署]MVC4.0+EF5.0+ODT+ORACLE相关注意事项
摘要 项目开发工具:VS2012旗舰版(.NetFrameWork4.5.1),WIN7 64bit,Oracle 11g 服务器环境:Windows Server2008 R2 64bit,.Net ...
- oracle疑难杂症问题
在虚拟机中安装了oracle10g,由于虚拟机的空间有限,看到磁盘空间快没了,就手贱把oracle目录中的空文件夹(E:\oracle\product\10.2.0\flash_recovery_ar ...
- OAF_开发系列26_实现OAF中Java类型并发程式开发oracle.apps.fnd.cp.request(案例)
20150730 Created By BaoXinjian
- 用事实说话,成熟的ORM性能不是瓶颈,灵活性不是问题:EF5.0、PDF.NET5.0、Dapper原理分析与测试手记
[本文篇幅较长,可以通过目录查看您感兴趣的内容,或者下载格式良好的PDF版本文件查看] 目录 一.ORM的"三国志" 2 1,PDF.NET诞生历程 2 2,Linq2 ...
- 实体类的枚举属性--原来支持枚举类型这么简单,没有EF5.0也可以
通常,我们都是在业务层和界面层使用枚举类型,这能够为我们编程带来便利,但在数据访问层,不使用枚举类型,因为很多数据库都不支持,比如我们现在用的SqlServer2008就不支持枚举类型的列,用的时候也 ...
- Entity Framework6 with Oracle(可实现code first)
Oracle 与2个月前刚提供对EF6的支持.以前只支持到EF5.EF6有很多有用的功能 值得升级.这里介绍下如何支持Oracle 一.Oracle 对.net支持的一些基础知识了解介绍. 1.早 ...
- Entity Framework6 with Oracle
Entity Framework6 with Oracle(可实现code first) Oracle 与2个月前刚提供对EF6的支持.以前只支持到EF5.EF6有很多有用的功能 值得升级.这里介绍下 ...
- ORACLE透明网关访问SQL Server配置总结
透明网关概念 ORACLE透明网关(Oracle Transparent Gateway)可以解决ORACLE数据库和非ORACLE数据库交互数据的需求.在一个异构的分布式环境中,通过ORACLE ...
- 用EF的三种方式(SqlServer数据库和Oracle数据库)
SqlServer数据库 1.DB First 现有DB,生成edmx文件 贴一下生成的model //------------------------------------------------ ...
随机推荐
- solr与.net课程(七)solr主从复制
既然solr是解决大量数据全文索引的方案,因为高并发的问题,我们就要考虑solr的负载均衡了,solr提供很easy的主从复制的配置方法,那么以下我们就来配置一下solr的主从复制 如果我们在192. ...
- dwz 照片回头处理
我的要求.要选择封面文章,回头一看,实现,查找回头功能bringBack代码中发现的,它们朝着input 标签处理,所以img总是标签不能显示,这么dwz源所做的更改,于dwz.databases.j ...
- Swift和C#的基本语法对比
Recently, Apple announced and released a beta version of the new Swift programming language for buil ...
- Socket方法LAN多线程文件传输
1.思维:为了实现各种文件的大小可以被发送和接收的,它可以被设置为发送和接收缓冲器环.并记录文件的位置读取,假设读入缓冲区的字节的特定数目大于缓冲区的大小较小.然后该文件被发送,退出发送周期,关闭连接 ...
- 初学者应学会如何加快seo
新手学习如何加快seo 介绍: 应该如何初学者学习SEO,前弯路.真正的高手SEO知识. 作为一个新人,学习如何加快seo知识吧? 多人天天都在学习seo知识.看别人的文章.看多了就感觉 ...
- 【翻译自mos文章】rman 备份时报:ORA-02396: exceeded maximum idle time
rman 备份时报:ORA-02396: exceeded maximum idle time 參考原文: RMAN backup faling with ORA-02396: exceeded ma ...
- Android应用公布的准备——生成渠道包
我们须要使用一个变量标明该app的渠道.通常我们能够在manifest中的application节点下声明.例如以下. <meta-data android:name="CHANNEL ...
- GoldenGate组态(四)它veridata组态
GoldenGate组态(四)它veridata组态 环境: Item Source System Target System Platform Red Hat Enterprise Linux Se ...
- jQuery -> 获取各种滤芯(filter)
按顺序选择 依次选择过滤器(filter)有着 :first 第一元件 :last 最后一个元素 :even 序号为偶数的元素 :odd 序号为奇数的元素 :eq(n) 序号等于n的元素 :lt(n) ...
- Golang基于学习总结
1.不支持继承 重载 ,比方C++Java的接口,接口的改动会影响整个实现改接口的类行为的改动,Go 设计者觉得这一特点也许根本没用. 2.必不论什么函数定义必须花括号跟在函数声明后面而不能换行 如 ...