• 利用动态代理实现通用存储过程的调用


       很久没有更新了,哈哈,最近太懒惰了,业余时间,写了一个关于存储过程的调用的东东,部分思想来自于Lostinet大大写的用 System.Reflection.Emit 自动实现调用存储过程的接口,他的实现是用Emit,我改用动态代理,其实内部都用到Emit

       通常情况下我们利用ADO.NET调用存储过程往往要写上好多代码,特别存储过程是参数很多的话很容易出错,而且很繁琐,看看下面这段调用存储过程的代码:

          晕,居然这么多,不知道你觉得烦不烦,反正我是很讨厌,很反感了。

          其实利用动态代理,可以解决很多问题,现在先假设我们调用的模式:
      1.定义调用的接口,方法名对应到要调用的存储过程名,参数也与之对应(注:我用的实例数据库是NorthWind):
        public interface ISproces
        
    {
            System.Data.DataSet CustOrderHist(
    string CustomerID);
            DataSet CustOrdersDetail(
    int OrderID);
        
            
    //如果储存过程名字和方法名字不同,应该用SpCustomNameAttribute来进行说明
            [SpCustomNameAttribute("Employee Sales By Country")]
            DataSet EmployeeSalesByCountry(DateTime Beginning_Date,DateTime Ending_Date);
        }
    2.利用Castle的DynamicProxy拦截对接口ISproces的调用,并写自己的拦截类SprocInterceptor,
    如果不清楚DynamicProxy请参见园子里的一些优秀的文章:DynamicProxy(动态代理)技术剖析(1) DynamicProxy(动态代理)技术剖析(2)
    /// <summary>
        
    /// 该类负责拦截接口中方法的执行,并调用对应的存储过程
        
    /// </summary>

        public class SprocInterceptor:StandardInterceptor
        
    {
            
    public SqlConnection connection;

            
    public SprocInterceptor()
            
    {
            }


            
    public override object Intercept(IInvocation invocation, params object[] args)
            
    {
                MethodInfo method
    =invocation.Method;
                connection.Open();
                
    string methodName="";
                
    object returnObj=null;
                
    if (invocation.Method.IsDefined(typeof(SpCustomNameAttribute),true))
                
    {
                    methodName 
    =SpCustomNameAttribute.GetSPName(method);
                }

                
    else
                
    {
                    methodName
    =method.Name;
                }

                SqlCommand command 
    = new SqlCommand(methodName, connection);
                command.CommandType 
    = System.Data.CommandType.StoredProcedure;
                ParameterInfo[] paramInfos
    =method.GetParameters();
                
    int paramlength=paramInfos.Length;
                
    for(int i=0;i<paramlength;i++)
                
    {
                    Type type
    =paramInfos[i].ParameterType;
                    SqlDbType sqlType
    =ConvertSqlType(type);
                    SqlParameter PageIndexParam 
    = command.Parameters.Add("@"+paramInfos[i].Name, sqlType);
                    PageIndexParam.Value 
    = args[i];
                }


                
    if(method.ReturnType==typeof(void))
                
    {
                    
    // 执行
                    command.ExecuteNonQuery();
                }

                
    else if(method.ReturnType==typeof(DataSet))
                
    {
                    
    // 取出数据集
                    SqlDataAdapter adapter = new SqlDataAdapter(command);
                    DataSet dataset 
    = new DataSet();
                    adapter.Fill(dataset);
                    adapter.Dispose();
                    returnObj
    =dataset;
                }

                
    else
                
    {
                    SqlParameter returnValueParam 
    = command.Parameters.Add("@returnValueParam",ConvertSqlType(method.ReturnType));
                    returnValueParam.Direction 
    = System.Data.ParameterDirection.ReturnValue;
                    
    // 执行
                    command.ExecuteNonQuery();
                    returnObj
    =Convert.ChangeType(returnValueParam.Value,method.ReturnType);
                }

                
    // 清除
                command.Dispose();
                connection.Close();
                
    return returnObj;
            
            }

    }


          该类继承自StandardInterceptor,并重写了Intercept方法实现对调用的方法的拦截,invocation得到调用的方法名,返回值,参数名,参数的类型而params object[] args参数对应的数据,得到这些数据后,我们便可以很轻松的构造对存储过程的ADO.NET调用的代码了,同时区分处理返回值和void的情况。
         值得注意的是:SqlParameter param = command.Parameters.Add("@"+paramInfos[i].Name, sqlType);这个sqlType是SQL server对应的数据类型的枚举,所以这里需要一个映射,使.net的Type转换到SqlDbType,很简单:

     1
     2        /// <summary>
     3        /// 转化类型到SQL server对应的数据类型
     4        /// </summary>
     5        /// <param name="type"></param>
     6        /// <returns></returns>

     7        public static SqlDbType ConvertSqlType(Type type) 
     8        
     9            if (type.FullName.ToLower() == "system.int64"
    10            
    11                return SqlDbType.BigInt; 
    12            }
     
    13            else if (type.FullName.ToLower() == "system.boolean"
    14            
    15                return SqlDbType.Bit; 
    16            }
     
    17            else if (type.FullName.ToLower() == "system.datetime"
    18            
    19                return SqlDbType.DateTime; 
    20            }
     
    21            else if (type.FullName.ToLower() == "system.decimal"
    22            
    23                return SqlDbType.Decimal; 
    24            }
     
    25            else if (type.FullName.ToLower() == "system.double"
    26            
    27                return SqlDbType.Float; 
    28            }
     
    29            else if (type.FullName.ToLower() == "system.int32"
    30            
    31                return SqlDbType.Int; 
    32            }
     
    33            else if (type.FullName.ToLower() == "system.single"
    34            
    35                return SqlDbType.Real; 
    36            }
     
    37            else if (type.FullName.ToLower() == "system.int16"
    38            
    39                return SqlDbType.SmallInt; 
    40            }
     
    41            else if (type.FullName.ToLower() == "system.byte"
    42            
    43                return SqlDbType.TinyInt; 
    44            }
     
    45            else if (type.FullName.ToLower() == "system.guid"
    46            
    47                return SqlDbType.UniqueIdentifier; 
    48            }
     
    49            else if (type.FullName.ToLower() == "system.byte()"
    50            
    51                return SqlDbType.VarBinary; 
    52            }
     
    53            else if (type.FullName.ToLower() == "system.string"
    54            
    55                return SqlDbType.VarChar; 
    56            }
     
    57            else if (type.FullName.ToLower() == "system.object"
    58            
    59                return SqlDbType.Variant; 
    60            }
     
    61            else 
    62            
    63                throw new ArgumentOutOfRangeException(); 
    64            }
     
    65        }

    3.需要给定义了存储过程方法的接口创建代理,使得拦截器去拦截其中的方法:

        /// <summary>
        
    /// InvokeSP 的摘要说明。
        
    /// </summary>

        public class SpProxy
        
    {
            
    //public SqlConnection connection;
            public SpProxy()
            
    {
            }


            
    public static object CreatSpObject(Type inerfaceType,SqlConnection connection)
            
    {
                ProxyGenerator _generator 
    = new ProxyGenerator(); 
                GeneratorContext context 
    = new GeneratorContext(); 
                SprocInterceptor interceptor 
    = new SprocInterceptor();
                interceptor.connection
    =connection;
                
    object proxy = _generator.CreateCustomProxy(inerfaceType, interceptor,new noUse(), context);
                
    return proxy;
            }


            
    private class noUse
            
    {
                
            }


        }

    4.如何调用呢??肯定有很多朋友都会问的。

            private void button2_Click(object sender, System.EventArgs e)
            
    {
                System.Data.SqlClient.SqlConnection connection
    =new System.Data.SqlClient.SqlConnection("Initial Catalog=Northwind;Data Source=(local);Packet Size=4096;user id=sa;password=sa");
                
    object proxy=SpProxy.CreatSpObject(typeof(ISproces),connection);
                ISproces sp
    =proxy as ISproces;
                DataSet ds
    =sp.CustOrdersDetail(10249);
                dataGrid1.DataSource
    =ds.Tables[0];
            }

    这样就可以对ISporces中定义的方法映射到对应名称的存储过程上去,实现调用。

    示例代码下载

  • 相关阅读:
    郝小亮-读王坚《在线》:互联网能做的和没做的事还有很多
    区块链的模型架构浅分析
    Springboot Application 集成 OSGI 框架开发
    java的OSGi确实是个坑
    所有围绕微信公众号,企业号开发者的企业或个人的创造的利润以及生存时间周期
    4张图让你看懂分布式架构从硬件到软件
    .NET J2EE APP全局会话架构运用场景
    ilspy导致c# dll代码被窃取
    windows+pytorch+pycharm配置
    海康-qt+opencv开发海康威视网络摄像机采集处理程序
  • 原文地址:https://www.cnblogs.com/jintan/p/731217.html
Copyright © 2020-2023  润新知