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 ...
随机推荐
- Apache中按天分割日志(Windows)
网上很多资料都有对Apache的access.log按天生成的方法,但在Windows server下稍有不同: 1.打开httpd.conf配置文件找到: CustomLog "logs/ ...
- Python实践练习:strip()的正则表达式版本
题目: 写一个函数,它接受一个字符串,做的事情和 strip()字符串方法一样.如果只传入了要去除的字符串,没有其他参数,那么就从该字符串首尾去除空白字符.否则,函数第二个参数指定的字符将从该字符串中 ...
- Spring MVC起步
1.1跟踪Spring MVC的请求 每当用户在Web浏览器中点击链接或提交表单的时候,请求就开始工作了.对请求的工作描述就像是快递投送员.与邮局投递员或FedEx投送员一样,请求会将信息从一个地方带 ...
- sysbench基准测试工具使用
1.源码编译安装 源码下载地址(目前有0.4/0.5/1.0三个分支版本):https://github.com/akopytov/sysbench 编译安装: unzip sysbench-1.0. ...
- leetcode703
class KthLargest { public: KthLargest(int k, vector<int> nums) { size = k; for(auto num:nums){ ...
- 基于 DirectX11 的 MMDViewer 03-渲染管线
准备工作: 开始搭建框架之前,你需要确保已经进行了 D3D 开发环境的搭建,相关教程可以阅读这篇文章.不了解 DirectX11 的人,这个作者有关 DirectX11 的教程最好阅读一下,虽然文章不 ...
- umbraco
在任意页面获取根节点 var locale = CurrentPage.Site(); 遍历根节点 @foreach (var module in CurrentPage.Site().Childre ...
- 4.2 最邻近规则分类(K-Nearest Neighbor)KNN算法应用
1 数据集介绍: 虹膜 150个实例 萼片长度,萼片宽度,花瓣长度,花瓣宽度 (sepal length, sepal width, petal length and petal wi ...
- 常用sql语句备份
1.查看数据库中所有的存储过程 use [DataBase_Name] go SELECT * FROM sys.all_objects WHERE ([type] = 'P' OR [type] = ...
- js原型和原型链[转]
附上原文出处:http://hzjavaeyer.group.iteye.com/group/wiki/3086-JavaScript-core-concepts 一.概念: 原型对象:JavaScr ...