用 System.Reflection.Emit 來自動生成調用儲存過程的實現 - 中國WEB開發者網絡 (http://www.webasp.net) -- 技術教程 (http://www.webasp.net/article/) --- 用 System.Reflection.Emit 來自動生成調用儲存過程的實現 (http://www.webasp.net/article/16/15113.htm) |
| -- 作者:未知 -- 發佈日期: 2004-11-26 |
| /****************************************************************\
* * 用 System.Reflection.Emit 來自動生成調用儲存過程的實現! * * By http://lostinet.com * * Copyrights : Not-Reversed * \****************************************************************/ //使用的例子 namespace Lostinet.Sample { using System; using System.Data; using System.Data.SqlClient; using System.Windows.Forms; //定義一個接口,用於定義存儲過程 interface INorthwindStoredProcedures { //定義存儲過程對應的方法 DataSet CustOrderHist(string CustomerID); //如果儲存過程名字和方法名字不同,應該用SqlAccessAttribute來進行說明 [SqlAccess("Employee Sales By Country")] DataTable EmployeeSalesByCountry(DateTime Beginning_Date,DateTime Ending_Date); //...more... //MORE Ideas.. //直接執行SQL語句? //[SqlAccess(SqlAccessType.SqlQuery,"SELECT * FROM Employees WHERE EmployeeID=@EmpId")] //DataTable SelectEmployee(int EmpId); } class ConsoleApplication { [STAThread] static void Main(string[] args) { using(SqlConnection conn=new SqlConnection("server=(local);trusted_connection=true;database=northwind")) { //一句話就把實現創建了! //需要傳如 SqlConnection 和 SqlTransaction //SqlTransaction可以為null //這個好就好在,只要能得到SqlConnection/SqlTransaction就能用這個方法了,所以兼容 Lostinet.Data.SqlScope INorthwindStoredProcedures nsp=(INorthwindStoredProcedures) StoredProcedure.CreateStoredProcedureInterface(typeof(INorthwindStoredProcedures),conn,null); //調用儲存過程並且顯示 ShowData("CustOrderHist ALFKI",nsp.CustOrderHist("ALFKI")); ShowData("Employee Sales By Country",nsp.EmployeeSalesByCountry(new DateTime(1998,1,1),new DateTime(1999,1,1))); } } static void ShowData(string title,object data) { Form f=new Form(); f.Width=600; f.Height=480; f.Text=title; DataGrid grid=new DataGrid(); grid.Dock=DockStyle.Fill; grid.DataSource=data; f.Controls.Add(grid); f.ShowDialog(); } } } #region //實現方法(不完整) namespace Lostinet.Sample { using System; using System.Collections; using System.Reflection; using System.Reflection.Emit; using System.Data; using System.Data.SqlClient; //這個類作為實現的基類, //目的是提供儲存 SqlConnection/SqlTransaction 和公用的一些方法 //這個類必須為public,否則無法繼承 //但開發者不會顯式訪問這個類 public class SPInterfaceBase : IDisposable { public SPInterfaceBase() { } public void Dispose() { } //CreateStoredProcedureInterface會把相關的值SqlConnection/SqlTransaction存到這裡 public SqlConnection connection; public SqlTransaction transaction; //創建一個SqlCommand public SqlCommand CreateCommand(string spname) { SqlCommand cmd=new SqlCommand(spname,connection,transaction); cmd.CommandType=CommandType.StoredProcedure; //TODO: //cmd.Parameters.Add("@ReturnValue",... return cmd; } //由 Type 推算出 SqlDbType , 未完成 SqlDbType GetSqlDbType(Type type) { //TODO:switch(type)... return SqlDbType.NVarChar; } //定義參數 public void DefineParameter(SqlCommand cmd,string name,Type type,ParameterDirection direction) { SqlParameter param=new SqlParameter("@"+name,GetSqlDbType(type)); param.Direction=direction; cmd.Parameters.Add(param); } //在SqlCommand執行前設置參數值 public void SetParameter(SqlCommand cmd,string name,object value) { cmd.Parameters["@"+name].Value=(value==null?DBNull.Value:value); } //在SqlCommand執行後取得參數值 public object GetParameter(SqlCommand cmd,string name) { return cmd.Parameters["@"+name].Value; } //根據不同的返回值執行不同的操作 public SqlDataReader ExecuteDataReader(SqlCommand cmd) { return cmd.ExecuteReader(); } public object ExecuteScalar(SqlCommand cmd) { return cmd.ExecuteScalar(); } public void ExecuteNonQuery(SqlCommand cmd) { cmd.ExecuteNonQuery(); } public DataSet ExecuteDataSet(SqlCommand cmd) { DataSet ds=new DataSet(); using(SqlDataAdapter sda=new SqlDataAdapter(cmd)) { sda.Fill(ds); } return ds; } public DataTable ExecuteDataTable(SqlCommand cmd) { DataTable table=new DataTable(); using(SqlDataAdapter sda=new SqlDataAdapter(cmd)) { sda.Fill(table); } return table; } public DataRow ExecuteDataRow(SqlCommand cmd) { DataTable table=ExecuteDataTable(cmd); if(table.Rows.Count==0) return null; return table.Rows[0]; } } public class StoredProcedure { static public object CreateStoredProcedureInterface(Type interfaceType,SqlConnection connection,SqlTransaction transaction) { //檢查參數 if(interfaceType==null)throw(new ArgumentNullException("interfaceType")); if(!interfaceType.IsInterface) throw(new ArgumentException("argument is not interface","interfaceType")); if(connection==null)throw(new ArgumentNullException("connection")); if(transaction!=null) { if(transaction.Connection!=connection) throw(new ArgumentException("transaction.Connection!=connection","transaction")); } //創建StoredProcedure StoredProcedure spemit=new StoredProcedure(); spemit.interfaceType=interfaceType; spemit.connection=connection; spemit.transaction=transaction; //創建 return spemit.CreateInstance(); } //用於儲存已創建的類型 static Hashtable EmittedTypes=new Hashtable(); Type interfaceType; SqlConnection connection; SqlTransaction transaction; private StoredProcedure() { } object CreateInstance() { lock(interfaceType) { //如果沒有創建具體的實現,則創建它 if(emittedType==null) { emittedType=(Type)EmittedTypes[interfaceType]; if(emittedType==null) { CreateType(); //儲存已創建類型 EmittedTypes[interfaceType]=emittedType; } } } //創建具體的實例 SPInterfaceBase spi=(SPInterfaceBase)Activator.CreateInstance(emittedType); //設置SqlConnection/SqlTransaction spi.connection=connection; spi.transaction=transaction; return spi; } Type emittedType; TypeBuilder typeBuilder; //創建類型 void CreateType() { //創建 Assembly //AssemblyBuilderAccess.Run-表示只用於運行,不在磁盤上保存 AssemblyName an=new AssemblyName(); an.Name="Assembly."+interfaceType.FullName+".Implementation"; AssemblyBuilder asmBuilder=AppDomain.CurrentDomain.DefineDynamicAssembly(an,AssemblyBuilderAccess.Run); //創建Module ModuleBuilder mdlBuilder=asmBuilder.DefineDynamicModule("Module."+interfaceType.FullName+".Implementation"); //創建Type,該類型繼承 SPInterfaceBase typeBuilder=mdlBuilder.DefineType(interfaceType.FullName+".Implementation",TypeAttributes.Class,typeof(SPInterfaceBase)); //實現所有的接口方法 EmitInterface(interfaceType); //如果interfaceType是基於其他接口的 foreach(Type subinterface in interfaceType.GetInterfaces()) { //IDisposable不需要實現,由SPInterfaceBase實現了 if(subinterface==typeof(IDisposable)) continue; EmitInterface(subinterface); } emittedType=typeBuilder.CreateType(); } void EmitInterface(Type type) { //實現接口 typeBuilder.AddInterfaceImplementation(type); //列出接口的成員 foreach(MemberInfo member in type.GetMembers(BindingFlags.Instance|BindingFlags.Public)) { //約定-成員必須是方法,不能有屬性啊,事件之類的 if(member.MemberType!=MemberTypes.Method) throw(new Exception("Could Not Emit "+member.MemberType+" Automatically!")); //取得接口中定義的方法 MethodInfo method=(MethodInfo)member; //計算新方法的屬性,在原來方法的屬性上複製過來,並且不是Public/Abstract,加上Private MethodAttributes methodattrs=method.Attributes; methodattrs&=~(MethodAttributes.Public|MethodAttributes.Abstract); methodattrs|=MethodAttributes.Private; ParameterInfo[] paramInfos=method.GetParameters(); int paramlength=paramInfos.Length; //取得參數的類型數組 Type[] paramTypes=new Type[paramlength]; for(int i=0;i<paramlength;i++) { paramTypes[i]=paramInfos[i].ParameterType; } //在typeBuilder上建立新方法,參數類型與返回類型都與接口上的方法一致 MethodBuilder mthBuilder=typeBuilder.DefineMethod(method.Name,methodattrs,method.CallingConvention,method.ReturnType,paramTypes); //複製新方法上的參數的名字和屬性 for(int i=0;i<paramlength;i++) { ParameterInfo pi=paramInfos[i]; //對於Instance,參數position由1開始 mthBuilder.DefineParameter(i+1,pi.Attributes,pi.Name); } //指定新方法是實現接口的方法的。 typeBuilder.DefineMethodOverride(mthBuilder,method); //在類型上定義一個字段,這個字段用於儲存被方法使用的SqlCommand FieldBuilder field_cmd=typeBuilder.DefineField("_cmd_"+method.Name,typeof(SqlCommand),FieldAttributes.Private); //ILGenerator 是用於生成實現代碼的對象 ILGenerator ilg=mthBuilder.GetILGenerator(); //定義臨時變量 LocalBuilder local_res=ilg.DeclareLocal(typeof(object)); //定義一個用於跳轉的Label Label label_cmd_ready=ilg.DefineLabel(); //this._cmd_MethodName ilg.Emit(OpCodes.Ldarg_0); //this ilg.Emit(OpCodes.Ldfld,field_cmd);//._cmd_MethodName //if(this._cmd_MethodName!=null) 跳到 label_cmd_ready ilg.Emit(OpCodes.Brtrue,label_cmd_ready); //如果this._cmd_MethodName為null,則運行下面代碼來創建SqlCommand //this._cmd_MethodName=this.CreateCommand("MethodName"); ilg.Emit(OpCodes.Ldarg_0); //this.CreateCommand ilg.Emit(OpCodes.Ldarg_0);//參數0 ilg.Emit(OpCodes.Ldstr,SqlAccessAttribute.GetSPName(method));//參數1 //調用 ilg.Emit(OpCodes.Callvirt,typeof(SPInterfaceBase).GetMethod("CreateCommand",BindingFlags.Instance|BindingFlags.Public)); ilg.Emit(OpCodes.Stfld,field_cmd);// ._cmd_MethodName= //this.DefineParameter(...) if(paramlength!=0) { //取得DefineParameter的引用 MethodInfo method_DefineParameter=typeof(SPInterfaceBase).GetMethod("DefineParameter",BindingFlags.Instance|BindingFlags.Public); for(int i=0;i<paramlength;i++) { //取得各參數 ParameterInfo pi=paramInfos[i]; //this.DefineParameter(this._cmd_MethodName,"ParameterName",typeof(ParameterType),ParameterDirection.Xxx); //參數0 - this ilg.Emit(OpCodes.Ldarg_0); //參數1 - this._cmd_MethodName ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldfld,field_cmd); //參數2 - "ParameterName" ilg.Emit(OpCodes.Ldstr,pi.Name); //參數3 - typeof(ParameterType) ilg.Emit(OpCodes.Ldtoken,pi.ParameterType); //參數4 - ParameterDirection.Xxx if(pi.ParameterType.IsByRef) { ilg.Emit(OpCodes.Ldc_I4,(int)ParameterDirection.InputOutput); } else if(pi.IsOut) { ilg.Emit(OpCodes.Ldc_I4,(int)ParameterDirection.Output); } else { ilg.Emit(OpCodes.Ldc_I4,(int)ParameterDirection.Input); } //調用DefineParameter ilg.Emit(OpCodes.Callvirt,method_DefineParameter); } } //到這裡 _cmd_CommandName 已經 OK 了。 //設置label_cmd_ready就指這裡 ilg.MarkLabel(label_cmd_ready); //cmd!=null now. if(paramlength!=0) { //現在要把方法的參數的值設置到SqlParameter上 MethodInfo method_SetParameter=typeof(SPInterfaceBase).GetMethod("SetParameter",BindingFlags.Instance|BindingFlags.Public); for(int i=0;i<paramlength;i++) { ParameterInfo pi=paramInfos[i]; //如果參數是 out 的,則不需要設置 if(!pi.ParameterType.IsByRef&&pi.IsOut) continue; //this.SetParameter(this._cmd_MethodName,"ParameterName",ParameterName); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldfld,field_cmd); ilg.Emit(OpCodes.Ldstr,pi.Name); //取得參數值,如果參數為ValueType,則Box到Object ilg.Emit(OpCodes.Ldarg,i+1); if(pi.ParameterType.IsValueType) ilg.Emit(OpCodes.Box,pi.ParameterType); ilg.Emit(OpCodes.Callvirt,method_SetParameter); } } //現在要執行儲存過程(執行SqlCommand)了 //這裡根據返回值類型判斷怎樣執行SqlCommand Type returnType=method.ReturnType; //如果是 void 的,則不需要返回值 bool nores=returnType==typeof(void); MethodInfo method_Execute=null; if(nores) { //不需要返回值 method_Execute=typeof(SPInterfaceBase).GetMethod("ExecuteNonQuery",BindingFlags.Instance|BindingFlags.Public); } else if(returnType==typeof(object)) { //返回object method_Execute=typeof(SPInterfaceBase).GetMethod("ExecuteScalar",BindingFlags.Instance|BindingFlags.Public); } else if(returnType==typeof(DataSet)) { //返回DataSet method_Execute=typeof(SPInterfaceBase).GetMethod("ExecuteDataSet",BindingFlags.Instance|BindingFlags.Public); } else if(returnType==typeof(DataTable)) { //返回DataTable method_Execute=typeof(SPInterfaceBase).GetMethod("ExecuteDataTable",BindingFlags.Instance|BindingFlags.Public); } else if(returnType==typeof(DataRow)) { //返回DataRow method_Execute=typeof(SPInterfaceBase).GetMethod("ExecuteDataRow",BindingFlags.Instance|BindingFlags.Public); } else { //返回其他類型 foreach(Type retInterface in returnType.GetInterfaces()) { //如果是返回IDataReader if(retInterface==typeof(IDataReader)) { //只支持SqlDataReader if(!returnType.IsAssignableFrom(typeof(SqlDataReader))) throw(new Exception("SqlDataReader could not convert to "+returnType.FullName)); method_Execute=typeof(SPInterfaceBase).GetMethod("ExecuteDataReader",BindingFlags.Instance|BindingFlags.Public); break; } } } //如果找不到適合的策略, if(method_Execute==null) { //TODO:當然,這裡應該有返回Int32,String,...的,不過懶得再寫了。 //拋出異常,提示不支持該返回類型,要作者改改:) throw(new NotSupportedException("NotSupport ReturnType:"+returnType.FullName)); } //this.ExecuteXXX(this._cmd_MethodName) ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldfld,field_cmd); ilg.Emit(OpCodes.Callvirt,method_Execute); //如果有返回值的,則是 //local_res=this.ExecuteXXX(this._cmd_MethodName) if(!nores) { if(returnType.IsValueType) ilg.Emit(OpCodes.Box,returnType); ilg.Emit(OpCodes.Stloc,local_res); } if(paramlength!=0) { //這裡處理ref/out的參數 MethodInfo method_GetParameter=typeof(SPInterfaceBase).GetMethod("GetParameter",BindingFlags.Instance|BindingFlags.Public); for(int i=0;i<paramlength;i++) { ParameterInfo pi=paramInfos[i]; //如果不是ref/out則跳過 if(!pi.ParameterType.IsByRef&&!pi.IsOut) continue; //ParameterName=this.GetParameter(this._cmd_Methodname,"ParameterName") ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldfld,field_cmd); ilg.Emit(OpCodes.Ldstr,pi.Name); ilg.Emit(OpCodes.Callvirt,method_GetParameter); //如果類型是值類型,則需要 Unbox if(pi.ParameterType.IsValueType) ilg.Emit(OpCodes.Unbox,pi.ParameterType); ilg.Emit(OpCodes.Starg,i+1); } } //如果是 void , 則直接 return; //否者是 return local_res , 如果返回值類型是ValueType,則需要Unbox if(!nores) { ilg.Emit(OpCodes.Ldloc,local_res); if(returnType.IsValueType) ilg.Emit(OpCodes.Unbox,returnType); } ilg.Emit(OpCodes.Ret); // //throw(new NotImplementedException()); // ilg.Emit(OpCodes.Newobj,typeof(NotImplementedException).GetConstructor(new Type[0])); // ilg.Emit(OpCodes.Throw); } } } public enum SqlAccessType { StoredProcedure //TODO: //,SqlQuery } [AttributeUsage(AttributeTargets.Method)] public class SqlAccessAttribute : Attribute { string _sp; public SqlAccessAttribute(string spname) { _sp=spname; } public string StoreProcedure { get { return _sp; } } static public string GetSPName(MethodInfo method) { if(method==null)throw(new ArgumentNullException("method")); object[] attrs=method.GetCustomAttributes(typeof(SqlAccessAttribute),false); if(attrs==null||attrs.Length==0) return method.Name; return ((SqlAccessAttribute)attrs[0]).StoreProcedure; } //TODO: // public SqlAccessAttribute(SqlAccessType type,string text) // { // // } } } #endregion |
| webasp.net |