Dapper完美兼容Oracle,执行存储过程,并返回结果集。

摘要:
当我刚使用Dapper时,我感觉非常棒。CURD、批处理操作、存储过程、事务等。然而,对于Oracle来说,这是没有问题的。在Dapper+Oracle单元测试的早期阶段,没有问题。也就是说,正常的Sql操作没有问题。但Oracle需要使用游标来输出结果集。为OracleParameter设置参数类型。DbType没有游标游标类型。]通过在线收集,我们发现Dapper确实支持Oracle,但没有用于调用Oracle存储过程的内容。

Dapper完美兼容Oracle,执行存储过程,并返回结果集。

这个问题,困扰了我整整两天。

刚刚用到Dapper的时候,感觉非常牛掰。特别是配合.net 4.0新特性dynamic,让我生成泛型集合,再转json一气呵成。

不过,各种ORM总有让人吐槽的地方。。。

比如,我之前在SqlServer上写测试,搞封装,没有任何问题。CURD、批量操作、存储过程、事物等。

可是以转到Oracle上,就出问题了【喂~不是说好的支持Oracle的么】

在写Dapper+Oracle单元测试的前期,是没有问题的,也就是说普通的Sql操作是没有任何问题的。

然后,我写到存储过程的单元测试的时候,就蛋疼了。

因为原版采用的DbType数据类型枚举。Sqlserver返回结果集并没有输出游标。

但是Oracle输出结果集,就需要用游标了。那么,这里问题就来了。给OracleParameter设置参数类型,DbType并没有Cursor游标类型

关于Dapper的文档也是不多,而且大部分都集中在SqlServer上,可能应为服务于.Net平台,比较侧重于微软的配套数据库。

好吧,问题来了,那就解决。反正是开源的。源代码都有。

先根据问题来搜索【我不喜欢用百度,因为百度搜出来一大堆不相关的东西,铜臭味太重。google在国内有无法访问,我就选择了Bing,结果效果还不错。】

经过网上搜集,发现Dapper确实是支持Oracle的,但是对于调用Oracle存储过程的内容却没有。

好吧,没有的话,先自己分析分析。

既然是参数类型不支持,那么换成支持的不就成了?

原版的是这样的:

1 DynamicParameters dp = new DynamicParameters();
2 dp.Add("RoleId", "1");
3 dp.Add("RoleName", "", DbType.String, ParameterDirection.Output);

这是Dapper原版中,声明parameter的部分,上面代码红色部分,就是指定参数类型。

在system.data.oracleclient 中,有OracleType这个枚举有Cursor类型。

然后,去查看 DynamicParameters 类,如下图:

Dapper完美兼容Oracle,执行存储过程,并返回结果集。第1张

可以看到,这个类,是实现了一个接口的。说明,原作者给我们预留了接口去自己实现其他内容。

继续看看接口:

Dapper完美兼容Oracle,执行存储过程,并返回结果集。第2张

接口的内容很简单,就是一个AddParameters方法。

那么,可以确定,上面的猜测是对的。

我们直接扩展实现这个接口就可以了。如图:

Dapper完美兼容Oracle,执行存储过程,并返回结果集。第3张

自己去创建一个实现了IDynamicParameters的类OracleDynamicParameters。

然后参照原作者提供的DynamicParameters类来实现这个接口。

最终修改版如下(代码多,展开了直接复制代码贴到你的文件里面):

public class OracleDynamicParameters : SqlMapper.IDynamicParameters {
        private readonly DynamicParameters _dynamicParameters = new DynamicParameters();

        private readonly List<OracleParameter> _oracleParameters = new List<OracleParameter>();

        public void Add(string name, object value = null, DbType dbType = DbType.AnsiString, ParameterDirection? direction = null, int? size = null) {
            _dynamicParameters.Add(name, value, dbType, direction, size);
        }

        public void Add(string name, OracleType oracleDbType, ParameterDirection direction) {
            var oracleParameter = new OracleParameter(name, oracleDbType) { Direction = direction };
            _oracleParameters.Add(oracleParameter);
        }

        public void Add(string name, OracleType oracleDbType, int size, ParameterDirection direction) {
            var oracleParameter = new OracleParameter(name, oracleDbType, size) { Direction = direction };
            _oracleParameters.Add(oracleParameter);
        }

        public void AddParameters(IDbCommand command, SqlMapper.Identity identity) {
            ((SqlMapper.IDynamicParameters)_dynamicParameters).AddParameters(command, identity);

            var oracleCommand = command as OracleCommand;

            if (oracleCommand != null) {
                oracleCommand.Parameters.AddRange(_oracleParameters.ToArray());
            }
        }

        public T Get<T>(string parameterName) {
            var parameter = _oracleParameters.SingleOrDefault(t => t.ParameterName == parameterName);
            if (parameter != null)
                return (T)Convert.ChangeType(parameter.Value, typeof(T));
            return default(T);
        }

        public T Get<T>(int index) {
            var parameter = _oracleParameters[index];
            if (parameter != null)
                return (T)Convert.ChangeType(parameter.Value, typeof(T));
            return default(T);
        }
    }

    public sealed class DbString {
        public DbString() { Length = -1; }
        public bool IsAnsi { get; set; }
        public bool IsFixedLength { get; set; }
        public int Length { get; set; }
        public string Value { get; set; }
        public void AddParameter(IDbCommand command, string name) {
            if (IsFixedLength && Length == -1) {
                throw new InvalidOperationException("If specifying IsFixedLength,  a Length must also be specified");
            }
            var param = command.CreateParameter();
            param.ParameterName = name;
            param.Value = (object)Value ?? DBNull.Value;
            if (Length == -1 && Value != null && Value.Length <= 4000) {
                param.Size = 4000;
            }
            else {
                param.Size = Length;
            }
            param.DbType = IsAnsi ? (IsFixedLength ? DbType.AnsiStringFixedLength : DbType.AnsiString) : (IsFixedLength ? DbType.StringFixedLength : DbType.String);
            command.Parameters.Add(param);
        }
    }

ok,扩展写完了,来一个单元测试,试一试:

 1         /// <summary>
 2         /// 执行带参数存储过程,并返回结果
 3         /// </summary>
 4         public static void ExectPro()
 5         {
 6             var p = new OracleDynamicParameters();
 7             p.Add("beginTime", 201501);
 8             p.Add("endTime", 201512);
 9             p.Add("targetColumn", "tax");
10             p.Add("vCur", OracleDbType.RefCursor, ParameterDirection.Output);
11             using (IDbConnection conn = new OracleConnection(SqlConnOdp))
12             {
13                 conn.Open();
14                 var aa = conn.Query("p_123c", param: p, commandType: CommandType.StoredProcedure).ToList();
15                 aa.ForEach(m => Console.WriteLine(m.C_NAME));
16             }
17             Console.ReadLine();
18         }

结果执行通过,并打印了首列的所有值。

那么,Dapper的简单扩展就完成了。

写在后面

补充说明: 我用的Oracle驱动是ODP.NET,.net是4.0

这个ODP.NET的Oracle.DataAccess.dll推荐从你的目标服务器,复制回来,不要用本地的,反正我用本地的,就提示外部程序错误。猜测是版本问题或者是位数问题。

相关参考文章

http://stackoverflow.com/questions/6212992/using-dapper-with-oracle

https://stackoverflow.com/questions/15943389/using-dapper-with-oracle-user-defined-types

http://stackoverflow.com/questions/7390015/using-dapper-with-oracle-stored-procedures-which-return-cursors

免责声明:文章转载自《Dapper完美兼容Oracle,执行存储过程,并返回结果集。》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Mac上的qemusystemaarch64占用太多内存Percona 5.7安装下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Oracle TYPE 不允许修改字段长度 解决方案

Oracle默认状态下是不准删除 定义为Type的,网上有绝决方案,但是没有具体的方法! 今天遇到此类问题,解决方案如下: 1、工具-->查找数据库对象(如图) 2、输入相关type名称,(注意红框部分) 3、删除下面的两个引用(注意备份这两个引用的原始内容)。 4、然后在修改type里面的内容。 5、保存后还原刚刚保存的引用。 6、成功。...

oracle 字符集

影响Oracle数据库字符集最重要的参数是NLS_LANG参数。 它的格式如下: NLS_LANG = language_territory.charset 它有三个组成部分(语言、地域和字符集),每个成分控制了NLS子集的特性。 其中: Language: 指定服务器消息的语言, 影响提示信息是中文还是英文 Territory: 指定服务器的日期和数字格...

Oracle升级前备份和失败回退

一、升级前备份 1、软件备份[root@localhost backup]# su - oracle [oracle@localhost ~]$ cd $ORACLE_HOME[oracle@localhost db_1]$ pwd/db/oracle/oracle/product/10.2.0/db_1[oracle@localhost db_1]$...

.NET链接Oracle 参数绑定问题

在.NET项目中链接Oracle使用的驱动是 Oracle.ManagedDataAccess.dll ,这里下载 所遇到的问题 使用存储过程一个参数没有问题,发现两个或两个以上会有参数没传过来的现象。 最后通过排查发现是没有添加参数绑定(问题找了好长时间,刚开始还以为驱动的问题+_+)。 需要设置设置属性 BindByName = true; 下面附上 ...

oracle 导入问题(imp)

oracle 导入问题(imp) 1.密码过期 [oracle @oracle ~]$ imp graph/graph@orcl file=/tmp/neo4j.dmp full=y; 解决方案: 使用plsql登陆,弹出窗口重新设置密码即可。 2.无导入权限 解决方案: [oracle @oracle ~]$ sqlplus '/as sysdba...

Oracle 11g RAC INS06006 Passwordless SSH connectivity not set up between the following node(s) 解决方法

一.问题描述        在VBox 上安装Oracle 11.2.0.1 的RAC, 在SSH 配置时报错: INS-06006: Passwordless SSHconnectivity not set up between the following node(s)   注意: 这里奇怪的表现是我们setup是成功,仅仅是Test 失败。 二...