在网上搜了一下“动态调用WebService”相信都能搜出上千篇文章,但是都出自同一个版本:使用ServiceDescriptionImporter导入wsdl然后进行动态编译,再调用相应的Method返回值。这种方法不足之处就是编译的时候可能会有些慢,毕竟是编译整个WebService,而且前台都是使用同一个方法传入调用的方法来进行调用的。再者,如果使用了Model,引用了WebService后的Model并非此Model,而且如果是List的话,那更差之千里了,返回的只能是数组。
本人经过思考,用AOP的原理实现了WebService的动态调用,实际上,是调用接口类的方法,然后使用反射得到该方法的返回值,参数等,然后再构造一个WebService的代理类,动态编译后调用返回值。接下来将一一介绍。
首先定义一个WebService如下。其中使用了FaibClass.Data数据框架。
Code 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.WebService13 { 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,即不操作。
Code 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 Test10 { 11 [DynamicWebService1("ATest_{0}")]12 public class ATest : DynamicWebService13 { 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 } 前台调用如下:
Code 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));12 下面将一一对每个类进行说明。
一、自定义代理属性 DynamicWebServiceAttribute。
Code 1 //******************************************************************* 2 // 模块:动态WebService属性的基类 3 // 日期:2009-8-23 0:24 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.Runtime.Remoting.Proxies;12 using System.Runtime.Remoting;13 using System.Runtime.Remoting.Contexts;14 using System.Runtime.Remoting.Activation;15 16 using FaibClass.Dynamic.Configuration;17 18 namespace FaibClass.Dynamic.WebService19 { 20 /// 21 /// 动态WebService属性的基类。22 /// 23 [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]24 public class DynamicWebServiceAttribute : ProxyAttribute25 { 26 string m_match;27 28 public DynamicWebServiceAttribute(string match)29 { 30 m_match = match;31 }32 33 /// 34 /// 创建实例。35 /// 36 /// 37 /// 38 public override MarshalByRefObject CreateInstance(Type serverType)39 { 40 DynamicWebServiceSettings setting = DynamicWebServiceConfiguration.Settings[this.GetType().FullName];41 //创建初始对象42 MarshalByRefObject mobj = base.CreateInstance(serverType);43 if (setting != null)44 { 45 RealProxy realProxy = new AspectDynamicWebServiceProxy(setting, m_match, serverType, mobj);46 //经过代理后返回透明代理47 return realProxy.GetTransparentProxy() as MarshalByRefObject;48 }49 return mobj;50 }51 52 /// 53 /// 匹配符。54 /// 55 public string Match56 { 57 get { return m_match; }58 set { m_match = value; }59 }60 }61 }62 客户端还要为每一个WebService定义一个DynamicWebServiceAttribute的继承类,如
Code 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 : DynamicWebServiceAttribute10 { 11 public DynamicWebService1(string match) : base(match){}12 }13 }14 就是ATest上的那个特性,该类再在app.config里定义相应的webservice调用参数,后面再介绍。这里的Match你可能发现了,就是webservice里方法名与ATest里的匹配方式。
二、代理处理类 AspectDynamicWebServiceProxy 核心就在这里了
Code 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 /// 32 /// 动态调用WebService的代理处理类。 33 /// 34 internal class AspectDynamicWebServiceProxy : RealProxy 35 { 36 MarshalByRefObject target; 37 DynamicWebServiceSettings m_setting; 38 string m_match; 39 40 /// 41 /// 构造代理类。 42 /// 43 /// 44 public AspectDynamicWebServiceProxy(Type myType) 45 : base(myType) 46 { 47 } 48 /// 49 /// 构造代理类。 50 /// 51 /// 52 /// 53 /// 54 /// 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 /// 90 /// 动态调用WebService中的方法。 91 /// 92 /// 方法名。 93 /// 方法参数。 94 /// 返回值。 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 else114 { 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 else159 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 else178 { 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 }209 在invoke中,拦截了ATest的调用方法,DyamicCallWebService进行分析并构造WebServicw的代理类代码,这里使用了缓存,第一次调用 方法都要经过编译,以后就不用了。
三、配置类
DynamicWebServiceSectionHandler
Code 1 //******************************************************************* 2 // 模块:WebService配置节的访问 3 // 日期:2009-8-12 9:22:24 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.Configuration;12 using System.Xml;13 14 namespace FaibClass.Dynamic.Configuration15 { 16 /// 17 /// 处理WebService配置节的访问。18 /// 19 public sealed class DynamicWebServiceSectionHandler : IConfigurationSectionHandler20 { 21 /// 22 /// 创建动态WebService节处理程序23 /// 24 /// 父对象。25 /// 上下文件对象。26 /// Xml节点。27 /// 28 public object Create(object parent, object configContext, XmlNode section)29 { 30 return new DynamicWebServiceConfiguration(section);31 }32 }33 }34 DynamicWebServiceConfiguration
Code 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.Configuration15 { 16 /// 17 /// 动态WebService配置。18 /// 19 public sealed class DynamicWebServiceConfiguration20 { 21 DynamicWebServiceDictionary m_dic;22 static DynamicWebServiceDictionary m_dic1;23 24 /// 25 /// 由XmlNode生成实例配置集合。26 /// 27 /// 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 settings46 { 47 get { return m_dic; }48 }49 50 public static DynamicWebServiceDictionary Settings51 { 52 get53 { 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 }67 DynamicWebServiceDictionary
Code 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.Configuration14 { 15 /// 16 /// 动态WebService配置字典。17 /// 18 public class DynamicWebServiceDictionary : DictionaryBase19 { 20 internal void Add(string key, DynamicWebServiceSettings setting)21 { 22 Dictionary.Add(key, setting);23 }24 25 /// 26 /// 获取本字典内的键集合。27 /// 28 public ICollection Keys29 { 30 get { return Dictionary.Keys; }31 }32 33 /// 34 /// 由键名返回对应的配置信息。35 /// 36 /// 37 /// 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 }51 DynamicWebServiceSettings
Code 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.Configuration15 { 16 /// 17 /// 动态WebService配置信息。18 /// 19 public class DynamicWebServiceSettings20 { 21 /// 22 /// 处理类型23 /// 24 public string Type;25 /// 26 /// WebService地址27 /// 28 public string Url;29 /// 30 /// 引用程序集31 /// 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 }42 ap.config配置如下
Code 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"> <file name="access1"> <instanceType>FaibClass.Data.OleDb, FaibClass.Data2instanceType> <filename>{APP}\1.xmlfilename> <path>//config/connectionpath> file> <file name="access2"> <instanceType>FaibClass.Data.OleDb, FaibClass.Data2instanceType> <interfaceType>SysXmlinterfaceType> <filename>1.xmlfilename> <path>//config/connectionpath> file> <file name="access3"> <instanceType>FaibClass.Data.OleDb, FaibClass.Data2instanceType> <interfaceType>AppXmlinterfaceType> <path>constrpath> file> <reg name="sqlserver"> <instanceType>FaibClass.Data.SqlServer, FaibClass.Data2instanceType> <registryKey>CurrentUserregistryKey> <subKey>Software\MicrosoftsubKey> <key>ckey> reg> <file name="sqlserver2005"> <instanceType>FaibClass.Data.SqlServer, FaibClass.Data2instanceType> <interfaceType>BinaryinterfaceType> <filename>{APP}\connection.binfilename> <path>Chinapath> file> <string name="sqlite"> <instanceType>FaibClass.Data.DataDriverConverter, FaibClass.Data2instanceType> <dataDriver> <assemblyName>System.Data.SQLiteassemblyName> <connectionType>System.Data.SQLite.SQLiteConnectionconnectionType> <commandType>System.Data.SQLite.SQLiteCommandcommandType> <parameterType>System.Data.SQLite.SQLiteParameterparameterType> <dataAdapterType>System.Data.SQLite.SQLiteDataAdapterdataAdapterType> <parameterPrefix>@parameterPrefix> dataDriver> <connectionString>Data source={App}BitracDB.db3;Pooling=TrueconnectionString> string> faibclass.data.instance> <appSettings> <add key="constr" value="Provider=Microsoft.Jet.OLEDB.4.0;Data Source={App}\test.mdb">add> appSettings>configuration>