zoukankan      html  css  js  c++  java
  • Dapper完美兼容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 类,如下图:

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

    继续看看接口:

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

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

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

    自己去创建一个实现了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

  • 相关阅读:
    更改数据库sa密码
    单例模式实现 (Singleton)
    linux特殊符号大全
    “增强现实与多媒体”博客园开张了
    关于.NET运行多个版本
    DevExpress V7.2.3源码 Salesman or Opium Up to you
    there is no bool datatype in oralce datatype system
    .NET时代,还有多少人读库源码 求FCL源码
    当学术界在解决其理论问题,工程界形成形成了一个已开发者为核心的生态链
    Div css 管理后台 简易Demo
  • 原文地址:https://www.cnblogs.com/likeli/p/4875341.html
Copyright © 2011-2022 走看看