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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM