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

   很久没有更新了,哈哈,最近太懒惰了,业余时间,写了一个关于存储过程的调用的东东,部分思想来自于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中定义的方法映射到对应名称的存储过程上去,实现调用。

示例代码下载

 

posted @ 2007-04-28 16:17  代码乱了  阅读(3345)  评论(6编辑  收藏  举报