将 Dapper 与返回游标的 Oracle 存储过程一起使用

Posted

技术标签:

【中文标题】将 Dapper 与返回游标的 Oracle 存储过程一起使用【英文标题】:Using Dapper with Oracle stored procedures which return cursors 【发布时间】:2011-11-15 10:38:04 【问题描述】:

如何将Dapper 与返回游标的Oracle 存储过程一起使用?

var p = new DynamicParameters();
p.Add("foo", "bar");
p.Add("baz_cursor", dbType: DbType.? , direction: ParameterDirection.Output);

这里,DbType 是 System.Data.DbType,它没有 Cursor 成员。我尝试过使用 DbType.Object,但这不适用于 OracleClient 和 OracleDataAcess。

使用 OracleType 或 OracleDbType 的可能方法是什么?

【问题讨论】:

我对 Oracle 游标不是很熟悉; AFAIK 我们没有为此添加任何特定支持。你能给我举个例子没有 dapper吗? 当然,看看这里:msdn.microsoft.com/en-us/library/ms971506.aspx#msdnorsps_topic8 解决方案在这里gist.github.com/vijaysg/3096151 【参考方案1】:

将this 类添加到您的项目中

您的代码应如下所示:-

        var p = new OracleDynamicParameters();
        p.Add("param1", pAuditType);
        p.Add("param2", pCommnId);
        p.Add("outCursor", dbType: OracleDbType.RefCursor, direction: ParameterDirection.Output);

        using (var multi = cnn.QueryMultiple("procedure_name", param: p, commandType: CommandType.StoredProcedure))
        
            var data = multi.Read();
            return data;
        

【讨论】:

链接已损坏。【参考方案2】:

感谢这里的解决方案。我使用一个简单的 DynamicParameter 装饰器用更少的代码实现了同样的事情:

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 = null, ParameterDirection? direction = null, int? size = null)
    
        dynamicParameters.Add(name, value, dbType, direction, size);
    

    public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction)
    
        var oracleParameter = new OracleParameter(name, oracleDbType, 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());
        
    

【讨论】:

【参考方案3】:

只是为了详细说明 Sams 的建议,这就是我想出的。请注意,此代码很脆弱,现在仅适用于 Oracle。

修改后的 Dapper 1.7

void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity)
    
        if (templates != null)
        
            foreach (var template in templates)
            
                var newIdent = identity.ForDynamicParameters(template.GetType());
                Action<IDbCommand, object> appender;

                lock (paramReaderCache)
                
                    if (!paramReaderCache.TryGetValue(newIdent, out appender))
                    
                        appender = SqlMapper.CreateParamInfoGenerator(newIdent);
                        paramReaderCache[newIdent] = appender;
                    
                

                appender(command, template);
            
        

        foreach (var param in parameters.Values)
        
            string name = Clean(param.Name);
            bool add = !((Oracle.DataAccess.Client.OracleCommand)command).Parameters.Contains(name);
            Oracle.DataAccess.Client.OracleParameter p;
            if(add)
            
                p = ((Oracle.DataAccess.Client.OracleCommand)command).CreateParameter();
                p.ParameterName = name;
             else
            
                p = ((Oracle.DataAccess.Client.OracleCommand)command).Parameters[name];
            

            var val = param.Value;
            p.Value = val ?? DBNull.Value;
            p.Direction = param.ParameterDirection;
            var s = val as string;
            if (s != null)
            
                if (s.Length <= 4000)
                
                    p.Size = 4000;
                
            
            if (param.Size != null)
            
                p.Size = param.Size.Value;
            
            if (param.DbType != null)
            
                p.DbType = param.DbType.Value;    
            
            if (add)
            
                if (param.DbType != null && param.DbType == DbType.Object)
                
                    p.OracleDbType = Oracle.DataAccess.Client.OracleDbType.RefCursor;
                    ((Oracle.DataAccess.Client.OracleCommand)command).Parameters.Add(p);
                
                else
                
                    ((Oracle.DataAccess.Client.OracleCommand)command).Parameters.Add(p);
                                       
            
            param.AttachedParam = p;
        
    

测试代码

class Program

    static void Main(string[] args)
    
        OracleConnection conn = null;
        try
        
            const string connString = "DATA SOURCE=XE;PERSIST SECURITY INFO=True;USER ID=HR;PASSWORD=Adv41722";

            conn = new OracleConnection(connString);
            conn.Open();


            var p = new DynamicParameters();
            p.Add(":dep_id", 60);
            p.Add(":employees_c", dbType: DbType.Object, direction: ParameterDirection.Output);
            p.Add(":departments_c", dbType: DbType.Object, direction: ParameterDirection.Output);
            // This will return an IEnumerable<Employee> // How do I return both result?
            var results = conn.Query<Employee>("HR_DATA.GETCURSORS", p, commandType: CommandType.StoredProcedure);



        
        catch (Exception exception)
        
            Console.WriteLine(exception);
            throw;
        
        finally
        
            if (conn != null && conn.State == ConnectionState.Open)
            
                conn.Close();
                            
        
        Console.WriteLine("Fininhed!");
        Console.ReadLine();
    


class Employee

    public int Employee_ID  get; set; 
    public string FIRST_NAME  get; set; 
    public string LAST_NAME  get; set; 
    public string EMAIL  get; set; 
    public string PHONE_NUMBER  get; set; 

【讨论】:

您可以使用上述代码和 QueryMultiple 从存储过程中获取多个输出(如 code.google.com/p/dapper-dot-net 的“多个结果”标题下给出的) 我认为这不会改变任何东西,至少在我记录此功能时,Dapper 尤其是 Oracle 存在更深层次的问题。 blog.vijay.name/index.php/2012/07/11/… 非常好的代码,我已经更正了,而且我对我的实现与那个实现有点尴尬。谢谢!顺便说一句,那是你的代码吗? Vijay,你能不能直接回答这个问题,以便我可以通过接受你的回答来结束这个问题?【参考方案4】:

你必须实现:

 public interface IDynamicParameters
 
    void AddParameters(IDbCommand command, Identity identity);
 

然后在AddParameters 回调中,您将IDbCommand 转换为OracleCommand 并添加数据库特定参数。

【讨论】:

谢谢,就是这样。我所做的是在 DynamicParameters 中传入一个 DbType.Object,然后在 AddParameters 实现中检查并将参数设置为 RefCursor 的 OracleDbtype。很高兴我不必为此修改太多 Dapper。 您如何以不编辑 sqlmapper 文件以使升级更顺畅的方式执行此操作,我注意到主类是部分的,但 SqlMapper.IDynamicParameters 中的实现DynamicParameters 未标记为虚拟。 @SPATEN 不关注,我真的不希望人们继承 DynamicParameters,IDynamicParameters 的独立实现应该没问题,dapper 调用接口 @SPATEN 我想你是在问这个:code.google.com/p/dapper-dot-net/issues/detail?id=69 谢谢 Sam,当我们讨论 Dapper 的时候,你能看一下对 conn.Query 的调用吗,有没有返回 N 个结果,例如conn.Query(...) 所以我们当前的示例返回 2 个游标。

以上是关于将 Dapper 与返回游标的 Oracle 存储过程一起使用的主要内容,如果未能解决你的问题,请参考以下文章

oracle存储过程返回游标,取值报错

oracle 存储过程执行动态SQL 返回结果给游标,外部程序获得dataset结果集。

oracle 如何返回多条记录

使用函数在oracle中存储游标

如何调用返回引用游标的Oracle存储过程

ibatis 怎么返回oracle游标