使用AOP動態調用WebService
在網上搜了一下“動態調用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 ///
21 /// 動態WebService屬性的基類。
22 ///
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 ///
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 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 ///
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 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 ///
17 /// 處理WebService配置節的訪問。
18 ///
19 public sealed class DynamicWebServiceSectionHandler : IConfigurationSectionHandler
20 {
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


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 ///
17 /// 動態WebService配置。
18 ///
19 public sealed class DynamicWebServiceConfiguration
20 {
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 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 ///
16 /// 動態WebService配置字典。
17 ///
18 public class DynamicWebServiceDictionary : DictionaryBase
19 {
20 internal void Add(string key, DynamicWebServiceSettings setting)
21 {
22 Dictionary.Add(key, setting);
23 }
24
25 ///
26 /// 獲取本字典內的鍵集合。
27 ///
28 public ICollection Keys
29 {
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


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 ///
17 /// 動態WebService配置信息。
18 ///
19 public class DynamicWebServiceSettings
20 {
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配置如下


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>