在网上搜了一下“动态调用WebService”相信都能搜出上千篇文章,但是都出自同一个版本:使用ServiceDescriptionImporter导入wsdl然后进行动态编译,再调用相应的Method返回值。这种方法不足之处就是编译的时候可能会有些慢,毕竟是编译整个WebService,而且前台都是使用同一个方法传入调用的方法来进行调用的。再者,如果使用了Model,引用了WebService后的Model并非此Model,而且如果是List的话,那更差之千里了,返回的只能是数组。
本人经过思考,用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));
12
下面将一一对每个类进行说明。
一、自定义代理属性 DynamicWebServiceAttribute。
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.WebService
19 {
20 /// <summary>
21 /// 动态WebService属性的基类。
22 /// </summary>
23 [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
24 public class DynamicWebServiceAttribute : ProxyAttribute
25 {
26 string m_match;
27
28 public DynamicWebServiceAttribute(string match)
29 {
30 m_match = match;
31 }
32
33 /// <summary>
34 /// 创建实例。
35 /// </summary>
36 /// <param name="serverType"></param>
37 /// <returns></returns>
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 /// <summary>
53 /// 匹配符。
54 /// </summary>
55 public string Match
56 {
57 get { return m_match; }
58 set { m_match = value; }
59 }
60 }
61 }
62
客户端还要为每一个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 }
209
在invoke中,拦截了ATest的调用方法,DyamicCallWebService进行分析并构造WebServicw的代理类代码,这里使用了缓存,第一次调用 方法都要经过编译,以后就不用了。
三、配置类
DynamicWebServiceSectionHandler
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.Configuration
15 {
16 /// <summary>
17 /// 处理WebService配置节的访问。
18 /// </summary>
19 public sealed class DynamicWebServiceSectionHandler : IConfigurationSectionHandler
20 {
21 /// <summary>
22 /// 创建动态WebService节处理程序
23 /// </summary>
24 /// <param name="parent">父对象。</param>
25 /// <param name="configContext">上下文件对象。</param>
26 /// <param name="section">Xml节点。</param>
27 /// <returns></returns>
28 public object Create(object parent, object configContext, XmlNode section)
29 {
30 return new DynamicWebServiceConfiguration(section);
31 }
32 }
33 }
34
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 }
67
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 }
51
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 }
42
<?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>