方法简介:用AOP的原理实现了WebService的动态调用,实际上,是调用接口类的方法,然后使用反射得到该方法的返回值,参数等,然后再构造一个WebService的代理类,动态编译后调用返回值。
首先定义一个WebService如下。其中使用了FaibClass.Data数据框架。
1 using System; 2 using System.Web; 3 using System.Web.Services; 4 using System.Web.Services.Protocols; 5 using System.Xml.Serialization; 6 using Test.Model; 7 using Test.DA; 8 using FaibClass.Data; 9 10 [WebService(Namespace = "http://tempuri.org/")] 11 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 12 public class Service : System.Web.Services.WebService 13 { 14 public Service () { 15 } 16 [WebMethod] 17 public TCompanyType ATest_GetCompanyType() 18 { 19 ATCompanyType da = new ATCompanyType(); 20 da.AccessOptions = AccessOptions.SubEntityList; 21 //排除引用实体属性 22 da.PropertyFilter = new DebarredAttributes(typeof(ReferenceEntityAttribute)); 23 //列出分类 24 return da.Get("Name='大类'", (string[])null); 25 } 26 [WebMethod] 27 public TCompany ATest_GetFirstCompany() 28 { 29 return new ATCompany().Get(null); 30 } 31 [WebMethod] 32 public TCompanies ATest_GetCompanies() 33 { 34 return new ATCompany().Select(); 35 } 36 [WebMethod] 37 [XmlInclude(typeof(TCompany))] 38 public bool ATest_Insert(TCompany info) 39 { 40 return true; 41 } 42 [WebMethod] 43 [XmlInclude(typeof(TCompanies))] 44 public bool ATest_InsertAll(TCompanies list) 45 { 46 return true; 47 } 48 [WebMethod] 49 public void ATest_TestNull() 50 { 51 } 52 private void ATest_ListSubType(TCompanyTypes list) 53 { 54 if (list == null) return; 55 foreach (TCompanyType type in list) 56 { 57 //该分类下的公司 58 ATest_ListSubCompany(type.Companies); 59 //该分类下的子类 60 ATest_ListSubType(type.SubCompanyTypes); 61 } 62 } 63 64 //列出分类公司下面的子公司 65 private void ATest_ListSubCompany(TCompanies companies) 66 { 67 if (companies == null) return; 68 foreach (TCompany company in companies) 69 { 70 ATest_ListSubCompany(company.SubCompanies); 71 } 72 } 73 }客户端也定义一个与之相似的类,暂将它称为接口类,因为它并不实现操作,只是为AOP调用提供方法信息,但是返回值都为null,即不操作。
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using FaibClass.Data; 5 using Test.Model; 6 7 using FaibClass.Dynamic.WebService; 8 9 namespace Test 10 { 11 [DynamicWebService1("ATest_{0}")] 12 public class ATest : DynamicWebService 13 { 14 public TCompanyType GetCompanyType() 15 { 16 return null; 17 } 18 public TCompany GetFirstCompany() 19 { 20 return null; 21 } 22 public TCompanies GetCompanies() 23 { 24 return null; 25 } 26 public bool Insert(TCompany info) 27 { 28 return false; 29 } 30 public bool InsertAll(TCompanies list) 31 { 32 return true; 33 } 34 public void TestNull() 35 { 36 } 37 } 38 }前台调用如下:
1 ATest test = new ATest(); 2 test.TestNull(); 3 TCompanyType type = test.GetCompanyType(); 4 ListSubType(type.SubCompanyTypes); 5 Console.WriteLine(test.GetFirstCompany().Name); 6 Console.WriteLine(test.GetCompanies()[0].Name); 7 TCompany a = new TCompany(); 8 a.Name = "dfdf"; 9 TCompanies list = new TCompanies(); 10 list.Add(a); 11 Console.WriteLine(test.InsertAll(list));下面将一一对每个类进行说明。
一、自定义代理属性 DynamicWebServiceAttribute。
客户端还要为每一个WebService定义一个DynamicWebServiceAttribute的继承类,如
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 5 using FaibClass.Dynamic.WebService; 6 7 namespace Test 8 { 9 internal class DynamicWebService1 : DynamicWebServiceAttribute 10 { 11 public DynamicWebService1(string match) : base(match){} 12 } 13 } 14就是ATest上的那个特性,该类再在app.config里定义相应的webservice调用参数,后面再介绍。这里的Match你可能发现了,就是 webservice里方法名与ATest里的匹配方式。
二、代理处理类 AspectDynamicWebServiceProxy 核心就在这里了
1 //******************************************************************* 2 // 模块:动态调用WebService的代理处理类 3 // 日期:2009-8-23 0:35 4 // 作者:Faib 5 // 版权:Copyright Faib Studio 2009 6 // 官网: http://www.faib.net.cn 7 // 邮箱:faib920@126.com 8 // 备注: 9 //******************************************************************* 10 using System; 11 using System.Text; 12 using System.Collections; 13 using System.Collections.Generic; 14 using System.Runtime.Remoting.Proxies; 15 using System.Runtime.Remoting.Messaging; 16 using System.Runtime.Remoting; 17 using System.Runtime.Remoting.Activation; 18 using System.Runtime.Remoting.Services; 19 using System.Web.Services.Description; 20 using System.Xml; 21 using System.CodeDom; 22 using System.CodeDom.Compiler; 23 using Microsoft.CSharp; 24 using System.Reflection; 25 26 using FaibClass.Data; 27 using FaibClass.Dynamic.Configuration; 28 29 namespace FaibClass.Dynamic.WebService 30 { 31 /// <summary> 32 /// 动态调用WebService的代理处理类。 33 /// </summary> 34 internal class AspectDynamicWebServiceProxy : RealProxy 35 { 36 MarshalByRefObject target; 37 DynamicWebServiceSettings m_setting; 38 string m_match; 39 40 /// <summary> 41 /// 构造代理类。 42 /// </summary> 43 /// <param name="myType"></param> 44 public AspectDynamicWebServiceProxy(Type myType) 45 : base(myType) 46 { 47 } 48 /// <summary> 49 /// 构造代理类。 50 /// </summary> 51 /// <param name="setting"></param> 52 /// <param name="match"></param> 53 /// <param name="myType"></param> 54 /// <param name="obj"></param> 55 public AspectDynamicWebServiceProxy(DynamicWebServiceSettings setting, string match, Type myType, MarshalByRefObject obj) 56 : base(myType) 57 { 58 this.m_setting = setting; 59 this.m_match = match; 60 target = obj; 61 } 62 63 public override IMessage Invoke(IMessage msg) 64 { 65 IMessage retMsg = msg; 66 if (msg is IConstructionCallMessage) 67 { 68 IConstructionCallMessage ccm = (IConstructionCallMessage)msg; 69 RemotingServices.GetRealProxy(target).InitializeServerObject(ccm); 70 ObjRef oRef = RemotingServices.Marshal(target); 71 RemotingServices.Unmarshal(oRef); 72 retMsg = EnterpriseServicesHelper.CreateConstructionReturnMessage 73 (ccm, (MarshalByRefObject)this.GetTransparentProxy()); 74 } 75 else if (msg is IMethodCallMessage) 76 { 77 IMethodCallMessage mcm = (IMethodCallMessage)msg; 78 MethodInfo minfo = mcm.MethodBase as MethodInfo; 79 //调用WebService返回值 80 object result = DyamicCallWebService(minfo, mcm.Args); 81 if (result == null) 82 retMsg = RemotingServices.ExecuteMessage(target, mcm); 83 else 84 retMsg = new ReturnMessage(result, null, 0, null, mcm); 85 } 86 return retMsg; 87 } 88 89 /// <summary> 90 /// 动态调用WebService中的方法。 91 /// </summary> 92 /// <param name="method">方法名。</param> 93 /// <param name="args">方法参数。</param> 94 /// <returns>返回值。</returns> 95 private object DyamicCallWebService(MethodInfo method, object[] args) 96 { 97 Type agentType; 98 object agent = null; 99 string[] assemblies = m_setting.Assemblies.Split('|'); 100 //调用的方法名 101 string methodName = method.Name; 102 if (!string.IsNullOrEmpty(m_match)) 103 { 104 methodName = string.Format(m_match, methodName); 105 } 106 // 缓存键 107 string key = DataCacheKeyManager.GetServiceKey(m_setting.Type, method); 108 if (DataCache<object>.ContainsKey(key)) 109 { 110 agent = DataCache<object>.Get(key); 111 agentType = agent.GetType(); 112 } 113 else 114 { 115 116 CSharpCodeProvider icc = new CSharpCodeProvider(); 117 CompilerParameters cp = new CompilerParameters(); 118 //引用程序集 119 cp.ReferencedAssemblies.Add("mscorlib.dll"); 120 cp.ReferencedAssemblies.Add("System.dll"); 121 cp.ReferencedAssemblies.Add("System.Web.Services.dll"); 122 cp.ReferencedAssemblies.Add("System.Xml.dll"); 123 cp.ReferencedAssemblies.Add("FaibClass.Data2.dll"); 124 cp.ReferencedAssemblies.Add("FaibClass.Dynamic.dll"); 125 foreach (string ass in assemblies) 126 { 127 cp.ReferencedAssemblies.Add(ass.Split(',')[1].Trim() + ".dll"); 128 } 129 130 StringBuilder classSource = new StringBuilder(); 131 //加入引用 132 classSource.Append("using System;\n"); 133 classSource.Append("using System.Text;\n"); 134 classSource.Append("using System.Web.Services;\n"); 135 classSource.Append("using System.Web.Services.Protocols;\n"); 136 classSource.Append("using System.Web.Services.Description;\n"); 137 classSource.Append("using System.Xml.Serialization;\n"); 138 classSource.Append("using System.Diagnostics;\n"); 139 classSource.Append("using System.ComponentModel;\n"); 140 classSource.Append("using System.CodeDom.Compiler;\n"); 141 classSource.Append("using FaibClass.Data;\n"); 142 classSource.Append("using FaibClass.Dynamic.WebService;\n"); 143 foreach (string ass in assemblies) 144 { 145 classSource.Append("using " + ass.Split(',')[0].Trim() + ";\n"); 146 } 147 classSource.Append("\n"); 148 //定义特性 149 classSource.Append("[WebServiceBinding(Name = \"ServiceSoap\", Namespace = \"http://tempuri.org/\"), XmlInclude(typeof(MarshalByRefObject)), DesignerCategory(\"code\"), DebuggerStepThrough, GeneratedCode(\"Test\", \"1.0.0.0\")]\n"); 150 classSource.Append("public class DynamicWebService : DynamicSoapHttpClientProtocol\n"); 151 classSource.Append("{\n\tpublic DynamicWebService(){ base.Url = \"" + m_setting.Url + "\"; }"); 152 //方法开始======= 153 classSource.Append("\n\t[SoapDocumentMethod(\"http://tempuri.org/" + methodName + "\", RequestNamespace = \"http://tempuri.org/\", ResponseNamespace = \"http://tempuri.org/\", Use=SoapBindingUse.Literal, ParameterStyle=SoapParameterStyle.Wrapped)]"); 154 classSource.Append("\n\tpublic "); 155 //方法返回类型 156 if (method.ReturnType.FullName == "System.Void") 157 classSource.Append("void"); 158 else 159 classSource.Append(method.ReturnType.Name); 160 //方法参数 161 classSource.Append(" " + methodName + "("); 162 bool first = true; 163 ParameterInfo[] pinfos = method.GetParameters(); 164 foreach (ParameterInfo pinfo in pinfos) 165 { 166 if (!first) classSource.Append(","); 167 else first = false; 168 classSource.Append(pinfo.ParameterType.Name + " " + pinfo.Name); 169 } 170 //================ 171 classSource.Append(")\n\t{\n\t\t"); 172 if (method.ReturnType.FullName != "System.Void") 173 classSource.Append("return (" + method.ReturnType.Name + ")"); 174 classSource.Append("base.Invoke(\"" + methodName + "\", "); 175 if (pinfos.Length == 0) 176 classSource.Append("new object[0]"); 177 else 178 { 179 classSource.Append("new object[]{"); 180 first = true; 181 foreach (ParameterInfo pinfo in pinfos) 182 { 183 if (!first) classSource.Append(","); 184 else first = false; 185 classSource.Append(pinfo.Name); 186 } 187 classSource.Append("}"); 188 } 189 classSource.Append(")"); 190 if (method.ReturnType.FullName != "System.Void") 191 classSource.Append("[0]"); 192 classSource.Append(";\n\t}\n}"); 193 194 CompilerResults cr = icc.CompileAssemblyFromSource(cp, classSource.ToString()); 195 int c = cr.Errors.Count; 196 agentType = cr.CompiledAssembly.GetTypes()[0]; 197 agent = Activator.CreateInstance(agentType); 198 DataCache<object>.Add(key, agent); 199 } 200 201 if (agent != null) 202 { 203 return agentType.GetMethod(methodName).Invoke(agent, args); 204 } 205 return null; 206 } 207 } 208 }在invoke中,拦截了ATest的调用方法,DyamicCallWebService进行分析并构造WebServicw的代理类代码,这里使用了缓存,第一次调用 方法都要经过编译,以后就不用了。
三、配置类
DynamicWebServiceSectionHandler
DynamicWebServiceConfiguration
1 //******************************************************************* 2 // 模块:动态WebService 3 // 日期:2009-8-23 0:51 4 // 作者:Faib 5 // 版权:Copyright Faib Studio 2009 6 // 官网: http://www.faib.net.cn 7 // 邮箱:faib920@126.com 8 // 备注: 9 //******************************************************************* 10 using System; 11 using System.Xml; 12 using System.Configuration; 13 14 namespace FaibClass.Dynamic.Configuration 15 { 16 /// <summary> 17 /// 动态WebService配置。 18 /// </summary> 19 public sealed class DynamicWebServiceConfiguration 20 { 21 DynamicWebServiceDictionary m_dic; 22 static DynamicWebServiceDictionary m_dic1; 23 24 /// <summary> 25 /// 由XmlNode生成实例配置集合。 26 /// </summary> 27 /// <param name="section"></param> 28 internal DynamicWebServiceConfiguration(XmlNode section) 29 { 30 if (section == null) throw new ArgumentNullException(); 31 32 m_dic = new DynamicWebServiceDictionary(); 33 foreach (XmlNode node in section.SelectNodes("webservice")) 34 { 35 string type = node.Attributes["type"] != null ? node.Attributes["type"].Value : ""; 36 string url = node.Attributes["url"] != null ? node.Attributes["url"].Value : ""; 37 string assemblies = node.Attributes["assemblies"] != null ? node.Attributes["assemblies"].Value : ""; 38 if (!string.IsNullOrEmpty(type) && !string.IsNullOrEmpty(url)) 39 { 40 m_dic.Add(type, new DynamicWebServiceSettings(type, url, assemblies)); 41 } 42 } 43 } 44 45 internal DynamicWebServiceDictionary settings 46 { 47 get { return m_dic; } 48 } 49 50 public static DynamicWebServiceDictionary Settings 51 { 52 get 53 { 54 if (m_dic1 == null) 55 { 56 object obj = ConfigurationManager.GetSection("faibclass.dynamic.webService"); 57 if (obj != null) 58 { 59 m_dic1 = ((DynamicWebServiceConfiguration)obj).settings; 60 } 61 } 62 return m_dic1; 63 } 64 } 65 } 66 }DynamicWebServiceDictionary
1 //******************************************************************* 2 // 模块:动态WebService配置字典 3 // 日期:2009-8-12 9:21:05 4 // 作者:Faib 5 // 版权:Copyright Faib Studio 2009 6 // 官网: http://www.faib.net.cn 7 // 邮箱:faib920@126.com 8 // 备注: 9 //******************************************************************* 10 using System; 11 using System.Collections; 12 13 namespace FaibClass.Dynamic.Configuration 14 { 15 /// <summary> 16 /// 动态WebService配置字典。 17 /// </summary> 18 public class DynamicWebServiceDictionary : DictionaryBase 19 { 20 internal void Add(string key, DynamicWebServiceSettings setting) 21 { 22 Dictionary.Add(key, setting); 23 } 24 25 /// <summary> 26 /// 获取本字典内的键集合。 27 /// </summary> 28 public ICollection Keys 29 { 30 get { return Dictionary.Keys; } 31 } 32 33 /// <summary> 34 /// 由键名返回对应的配置信息。 35 /// </summary> 36 /// <param name="key"></param> 37 /// <returns></returns> 38 public DynamicWebServiceSettings this[string key] 39 { 40 get 41 { 42 if (Dictionary.Contains(key)) 43 { 44 return (DynamicWebServiceSettings)Dictionary[key]; 45 } 46 return null; 47 } 48 } 49 } 50 }DynamicWebServiceSettings
1 //******************************************************************* 2 // 模块:动态WebService配置信息 3 // 日期:2009-8-23 0:12 4 // 作者:Faib 5 // 版权:Copyright Faib Studio 2009 6 // 官网: http://www.faib.net.cn 7 // 邮箱:faib920@126.com 8 // 备注: 9 //******************************************************************* 10 using System; 11 using System.Collections.Generic; 12 using System.Text; 13 14 namespace FaibClass.Dynamic.Configuration 15 { 16 /// <summary> 17 /// 动态WebService配置信息。 18 /// </summary> 19 public class DynamicWebServiceSettings 20 { 21 /// <summary> 22 /// 处理类型 23 /// </summary> 24 public string Type; 25 /// <summary> 26 /// WebService地址 27 /// </summary> 28 public string Url; 29 /// <summary> 30 /// 引用程序集 31 /// </summary> 32 public string Assemblies; 33 34 public DynamicWebServiceSettings(string type, string url, string assemblies) 35 { 36 Type = type; 37 Url = url; 38 Assemblies = assemblies; 39 } 40 } 41 }app.config配置如下
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="faibclass.data.instance" type="FaibClass.Data.Configuration.DataInstanceSectionHandler, FaibClass.Data2" /> <section name="faibclass.dynamic.webService" type="FaibClass.Dynamic.Configuration.DynamicWebServiceSectionHandler, FaibClass.Dynamic"/> </configSections> <faibclass.dynamic.webService> <webservice type="Test.DynamicWebService1" url="http://localhost:1574/WebService/Service.asmx" assemblies="Test.Model, Model" /> </faibclass.dynamic.webService> <faibclass.data.instance defaultInstance="sqlite"> <!--本地目录下的xml文件--> <file name="access1"> <instanceType>FaibClass.Data.OleDb, FaibClass.Data2</instanceType> <filename>{APP}\1.xml</filename> <path>//config/connection</path> </file> <!--系统目录下的xml文件--> <file name="access2"> <instanceType>FaibClass.Data.OleDb, FaibClass.Data2</instanceType> <interfaceType>SysXml</interfaceType> <filename>1.xml</filename> <path>//config/connection</path> </file> <!--本配置文件内--> <file name="access3"> <instanceType>FaibClass.Data.OleDb, FaibClass.Data2</instanceType> <interfaceType>AppXml</interfaceType> <path>constr</path> </file> <!--注册表内--> <reg name="sqlserver"> <instanceType>FaibClass.Data.SqlServer, FaibClass.Data2</instanceType> <registryKey>CurrentUser</registryKey> <subKey>Software\Microsoft</subKey> <key>c</key> </reg> <!--二进制文件--> <file name="sqlserver2005"> <instanceType>FaibClass.Data.SqlServer, FaibClass.Data2</instanceType> <interfaceType>Binary</interfaceType> <filename>{APP}\connection.bin</filename> <path>China</path> </file> <!--自定义驱动--> <string name="sqlite"> <instanceType>FaibClass.Data.DataDriverConverter, FaibClass.Data2</instanceType> <dataDriver> <assemblyName>System.Data.SQLite</assemblyName> <connectionType>System.Data.SQLite.SQLiteConnection</connectionType> <commandType>System.Data.SQLite.SQLiteCommand</commandType> <parameterType>System.Data.SQLite.SQLiteParameter</parameterType> <dataAdapterType>System.Data.SQLite.SQLiteDataAdapter</dataAdapterType> <parameterPrefix>@</parameterPrefix> </dataDriver> <connectionString>Data source={App}BitracDB.db3;Pooling=True</connectionString> </string> </faibclass.data.instance> <appSettings> <add key="constr" value="Provider=Microsoft.Jet.OLEDB.4.0;Data Source={App}\test.mdb"></add> </appSettings> </configuration>