EF6 CodeFisrt支持Oracle
EF6 CodeFisrt支持Oracle
EF说是支持多数据库,但真做起来太多坑了,编程这个词以后要换换,叫填坑好了。这次把我在做EF6 CodeFisrt支持Oracle数据库过程中遇到的坑写下来,给需要的人减少点填坑的痛苦。
先说下使用环境
- EF6.1.3
- CodeFirst
- Oracle版本我用的是11.2
- Oracle Provider用的是Oracle官方的ODP.NET, Managed Driver
Oracle官方文档
http://docs.oracle.com/cd/E56485_01/win.121/e55744/entityCodeFirst.htm#ODPNT8309
搭建环境
这里只说使用代码的配置方式,App.config或Web.config配置方式参照官方文档做就好。
1. 下载Oracle Provider
在vs的管理解决方案的NuGet程序包中搜索Oracle,找到ODP.NET(这是个简写),有两个,忽略Unmanaged,下载带Managed的。
2. 添加dll引用
在相关项目中添加下面两个dll引用:
Oracle.ManagedDataAccess.dll
Oracle.ManagedDataAccess.EntityFramework.dll
3. SetProvider
定义DbMigrationsConfiguration类
internal sealed class MyMigrationsConfiguration : DbMigrationsConfiguration<MyContext>
{
public DbConfiguration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(T context)
{
//种子数据
}
}
定义DbConfiguration类
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
SetDefaultConnectionFactory(new OracleConnectionFactory());
SetProviderServices("Oracle.ManagedDataAccess.Client", EFOracleProviderServices.Instance);
SetProviderFactory("Oracle.ManagedDataAccess.Client", new OracleClientFactory());
}
}
给你的DbContext类添加Attribute
[DbConfigurationType(typeof(EFConfiguration))]
public class MyContext : DbContext
{
}
4. 创建数据库
使用下面两段代码之一初始化数据库:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyMigrationsConfiguration>());
using (var ctx = new MyContext())
{
ctx.Database.Initialize(true);
}
或
new DbMigrator(new MyMigrationsConfiguration()).Update();
网上的几乎所有的例子都是让人在NuGet命令行中敲命令升级数据库,试问客户现场的生产数据库如何去升级?所以我们使用自动迁移,必须做到在没有开发人员参与下,由实施人员甚至是用户自己去点击个按钮,就自动根据实体去创建或者修改数据库。
- 不出意外,这时候肯定会出错,出什么错都有可能,我遇到的错误是:
System.InvalidOperationException: 序列不包含任何匹配元素
通过翻看EF的源码,发现是实体类定义中用了ColumnAttribute.TypeName指定了SQLServer中的类型,如“NVarChar(4000)”,改用StringLength去指定长度,另外发现如果设置为int.MaxValue,它在sqlserver上会是NVarChar(MAX)类型,在Oracle上是NCLOB。
- 继续运行,出下面或者类似的错误:
ORA-01918: 用户'SCOTT'不存在解决
解决:在Oracle中添加用户,然后在MyContext类中加入下面代码
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("你的Oracle用户名");
}
注意:添加Oracle用户时至少:设置Unlimited Tablespace、Create Session权限和resource角色;
- 继续运行,错误:
不支持影响迁移历史记录系统表的位置的自动迁移(例如默认架构更改)。对于影响迁移历史记录系统表的位置的操作,请使用基于代码的迁移。
翻看EF的源码,发现它会判断Schema是否为默认Schema,而默认的Schema定义的是一个常量“dbo”。同时ODP.NET文档中也写了必须是“dbo”Schema。
Code First Automatic Migrations is limited to working with the dbo schema only. Due to this limitation it is recommended to use
code-based migrations, that is, add explicit migrations through the
Add-Migration command.
这么问题就来了,无法自动迁移怎么办,我现在的解决办法是只能使用小写的“dbo”用户,这样很肯定为客户现场的部署带来未知的麻烦。我之前还准备修改EF源码,去掉这个默认Schema的限制,重新编译它,但又当心他们这么做是某些硬性条件导致,所以也就没去尝试,如果有人知道怎么解决这个限制还请告知。
如果你不需要自动迁移,那么可能问题简单很多,你可以自由的使用Oracle用户名。不过有可能你需要给__MigrationHistory表的实体HistoryRow设置Schema,做法很简单,参考https://msdn.microsoft.com/en-us/library/dn456841(v=vs.113).aspx
- 使用小写“dbo”做Oracle用户名
Oracle中所有名称都默认是大写的,如果需要区分大小写或者说是按你输入原文做名称,就需要加双引号,sql语句中也是同样。所以建小写“dbo”用户名的时候加英文的双引号就好。
使用小写“dbo”做用户名后,自动迁移就顺利了,当然错误还是会有的,根你软件的复杂情况有关系,比较容易出现的错误如下,都比较好解决:
ORA-00972: 标识符过长
Oracle限制似乎是所有名称30个字符,你可能有表名超过了限制。
ORA-00955: 名称已由现有对象使用
表名等被使用了,可能的原因是__MigrationHistory表中记录的上一次迁移没有某个表,但实际创建成功,出现的可能性很小,我是遇到了。
ORA-02264: 名称已被一现有约束条件占用
这个是因为某个表的同一个列加了多个外键导致,第一个外键约束会创建成功,第二个外键约束会使用相同名称导致重名。
- 数据库函数、过程等
我是在Seed方法中用DbContext对象的Database.ExecuteSqlCommand去创建函数、过程、触发器等的sql脚本的,这块出的问题比较单一了,都是sql语句的错,自行解决就好。
ODP.NET是声明不支持表值函数的,我在拦截器中修改sql让它支持,做法如下:
定义拦截器类:
public class OracleInterceptor : IDbCommandInterceptor
{
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
NReplace(command, interceptionContext.ObjectContexts.First());
}
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
NReplace(command, interceptionContext.ObjectContexts.First());
}
private static void NReplace(DbCommand command, ObjectContext ctx)
{
if (!command.CommandText.StartsWith("CREATE OR REPLACE"))
{
foreach (var item in ctx.MetadataWorkspace.GetItems<EdmFunction>(DataSpace.SSpace).Where(i => i.NamespaceName == "CodeFirstDatabaseSchema"))
{
if (item.ReturnParameter == null || item.ReturnParameter.TypeUsage.EdmType.BuiltInTypeKind != BuiltInTypeKind.CollectionType) continue;
var strs = new List<string>();
var methodName = item.Name;
var str = string.Format(@"""dbo"".""{0}""", methodName);
var i = 0;
while ((i = command.CommandText.IndexOf(str, i)) >= 0)
{
var j = i + str.Length;
var m = 0;
for (; j < command.CommandText.Length; j++)
{
if (command.CommandText[j] == '(')
{
m++;
}
else if (command.CommandText[j] == ')')
{
m--;
}
if (m == 0)
{
break;
}
}
strs.Add(command.CommandText.Substring(i, j - i + 1));
i = j;
}
foreach (var s in strs)
{
command.CommandText = command.CommandText.Replace(s, string.Format("table({0})", s));
}
}
}
}
}
然后在MyConfiguration构造函数中加一行
AddInterceptor(new OracleInterceptor());
5. Linq To Entities
在数据库创建并可以更新成功后,就开始把软件跑起来了,这个时候出的问题最多的就是Linq编译出来的sql执行错误。我遇到的错误有下面几个:
ORA-12704: 字符集不匹配
这个一般是因为,非Unicode字符串被当成Unicode字符串使用,常见于带有单引号字符串的sql中,并且可能存在字符串与字段连接操作,需要将'str'改成N'str'才行。
解决办法是:给DbConfiguration类中添加一个拦截器,拦截器类中用正则去找出字符串,全部替换成带前缀N。
ORA-00932: 数据类型不一致: 应为 NCHAR, 但却获得 NCLOB
这个错误也在拦截器中替换TO_NCLOB为TO_NCHAR。
OUTER APPLY not supported by Oracle
Oracle可能是没有OUTER APPLY这样的写法,但Linq转出来的Sql却总是含有它,导致大量的Linq出错,没办法,只能一点点改了,看官有好办法麻烦告知。
这个错误一般都是linq中有子查询,并且子查询有join或者子查询还有其他子查询,也可能Include方法也会导致这个问题,我现在还是换个写法来解决,如改成left join。
EF6 CodeFisrt支持Oracle的更多相关文章
- BI测试工具之跨数据库数据对比,支持oracle,sqlserver
应用场景: 本周在进行SIT,我帮助仅有的一个测试妹妹对部分表进行数据质量验证,第一步需要做的就是比对source与stage表的table definition 与 数据内容的一致性. 本项目使用的 ...
- [VSTS]让ADO.NET Entity Framework支持Oracle数据库(转载)
近期由于项目所需不得不研究Oracle数据库,回想上一次用Oracle还是07年的事情,实习时候做华晨宝马的项目简单接触了Oracle.这次的项目需要基于.NET平台,我个人的习惯是能用微软自带的就不 ...
- dapper支持oracle游标
dapper支持oracle游标 Dapper是一个轻型的ORM类.它有啥优点.缺点相信很多朋友都知道了,园里也有很多朋友都有相关介绍,这里就不多废话. 如果玩过Oracle都知道,存储过程基本都是通 ...
- Mybatis Generator的model生成中文注释,支持oracle和mysql(通过实现CommentGenerator接口的方法来实现)
自己手动实现的前提,对maven项目有基本的了解,在本地成功搭建了maven环境,可以参考我之前的文章:maven环境搭建 项目里新建表时model,mapper以及mapper.xml基本都是用My ...
- 让ADO.NET Entity Framework支持Oracle数据库
Oracle最近发布了 Oracle Data Access Component(ODAC)11. 2 Rel 4,其中增加了对 Entity Framework 4.1 和4.2的支持.这让 .NE ...
- 支持Oracle的模糊查询和精准查询
相信所有的软件开发者都做过页面上的查询功能,而且很多都需要既支持模糊查询的,也需要支持精准查询的,而且不需要增加多余的功能,只需要在文本框中输入包含类似*之类的符号即可. 下面的方法就是通过*来判断到 ...
- phalcon: 解决php7/phalcon3.2以上版本,不支持oracle数据库的方法
解决php7/phalcon3.2以上版本,不支持oracle数据库的方法 phalcon3.2(3.0以上)版本不支持oracle的方法. https://github.com/phalcon/in ...
- 行云管家 V4.7产品新特性-国际化版本、支持Oracle的数据库审计、主机密码自动修改策略 发布日期:2018-11-22
行云管家在线体验: 行云管家[官网]-领先的云计算管理平台-云安全,堡垒机,自动化运维 行云管家新手有礼活动: 行云管家新手有礼,新用户1元即可体验专业版-优惠券 发布日期:2018-11-22 ...
- 【翻译自mos文章】OGG的集成捕捉模式支持Oracle database标准版么?
OGG的集成捕捉模式支持Oracle database标准版么? 来源于: Does OGG 11.2.1 Integrated Capture Work with Oracle Database S ...
随机推荐
- python学习笔记(十四): unittest
Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作. 在说unittest之前,先说几个概念: TestC ...
- Pymol
如何用Pymol做出那些美呆的结构图(基础篇) 2016-10-31 翾园 摘自 BioEngX生化... 阅 1079 转 6 转藏到我的图书馆 微信分享: 摘自微信公众号:BioE ...
- oracle用户具有的权限和角色
如何查看一个oracle用户具有的权限和角色 1.查看所有用户: select * from dba_users; select * from all_users; select * from use ...
- [Z]LaTeX入门教程
LaTeX入门教程 Contents TEX/LATEX是什么? 为什么要用TEX/LATEX? 安装 开始使用 数学符号使用中文文章的各个部分表格 行内公式与行间公式 上标与下标 常见的数学公式 行 ...
- winform频繁刷新导致界面闪烁解决方法
转自龙心文 原文 winform频繁刷新导致界面闪烁解决方法 一.通过对窗体和控件使用双缓冲来减少图形闪烁(当绘制图片时出现闪烁时,使用双缓冲) 对于大多数应用程序,.NET Framework 提供 ...
- mybatis与springdata的一些简单比较与思考
主题 最近在用mybatis做项目,有一些感触想记录下,主要是mybatis(以及它的一些插件)相比较于Spring data(或者jpa,hibernate等)的优势地方. 感触 我觉得mybati ...
- python:一个轻松的递归逻辑
#递归 age = 10 def dig(n): global age#函数dig引用全局变量age age += 2 n -= 1 if n != 1:#如果满足条件,则调用本身 dig(n) di ...
- fastjson集合转字符串
JSON.toJSONString(list, SerializerFeature.DisableCircularReferenceDetect); list为集合
- minio test
docker pull minio/minio docker run --name=minio -d -p 9001:9000 minio/minio server /data https://do ...
- Creating Cubemaps in Unity3D
[Creating Cubemaps in Unity3D] 1.在Editor目录下生成GenerateStaticCubemap.cs. 2.编写代码,生成一个继承于ScriptableWizar ...