添加Web引用的時(shí)候,WebService在客戶端有一個(gè)代理,如下:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.42")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="WebService1Soap", Namespace="http://tempuri.org/")
public partial class WebService1 : System.Web.Services.Protocols.SoapHttpClientProtocol
客戶端調(diào)用WebServivce就是通過(guò)這個(gè)代理類來(lái)調(diào)用的。
2. 調(diào)用WebService方法,客戶端和服務(wù)器端通信是Xml,所以代理類跟Xml之間就有序列化和反序列化的過(guò)程
3. 客戶端調(diào)用WebService的過(guò)程如下
a) 客戶端調(diào)用代理類Hello world方法
string str = (new Service2.WebService1()).HelloWorld ();
b) 代理類調(diào)用基類SoapHttpClientProtocal的Invoke方法
public string HelloWorld() {
object[] results = this.Invoke("HelloWorld", new object[0]);
return ((string)(results[0]));
}
c) SoapHttpClientProtocal進(jìn)行Soap序列化Soap頭和方法,都是這個(gè)類自己做的,但是輸入?yún)?shù)和返回值,是利用的XmlSerializer,輸入?yún)?shù)要序列化,返回值要反序列化。
protected object[] Invoke(string methodName, object[] parameters)
{
…
try
{
message1.SetStream(stream1);
this.Serialize(message1);//注1
}
…
response1 = this.GetWebResponse(request1);
Stream stream2 = null;
try
{
stream2 = response1.GetResponseStream();
objArray1 = this.ReadResponse(message1, response1, stream2, false);//注2
}
}
注1:this.Serialize中有一句參數(shù)序列化的代碼如下
method1.parameterSerializer.Serialize(writer1, message.GetParameterValues(), null, flag1 ? text2 : null);
注2:this.ReadResponse中有一句返回值的反序列化的代碼如下
message.SetParameterValues((object[]) method1.returnSerializer.Deserialize(reader1, flag1 ? text1 : null));
d) XmlSerializer會(huì)緩存臨時(shí)程序集,這個(gè)程序集作用是序列化和反序列化,如果緩存中沒(méi)有會(huì)調(diào)用TempAssembly產(chǎn)生一個(gè)
Static的緩存(就是我們每次調(diào)用慢的罪魁禍?zhǔn)祝?/span>private static TempAssemblyCache cache;
獲取緩存中的程序集:this.tempAssembly = XmlSerializer.cache[defaultNamespace, type];
緩存中沒(méi)有就去加載:Assembly assembly1 = TempAssembly.LoadGeneratedAssembly(type, defaultNamespace, out implementation1);
加載沒(méi)有就去產(chǎn)生(會(huì)生成臨時(shí)文件并編譯,很慢):
this.tempAssembly = new TempAssembly(new XmlMapping[] { this.mapping }, assembly1, implementation1);
e) TempAssemlby這個(gè)類負(fù)責(zé)加載以及產(chǎn)生臨時(shí)程序集
在LoadGeneratedAssemlby方法中,有一段邏輯,就是默認(rèn)去加載序列化類,這個(gè)類的命名是規(guī)則如下
internal static string GetTempAssemblyName(AssemblyName parent, string ns)
{
return (parent.Name + ".XmlSerializers" + (((ns == null) || (ns.Length == 0)) ? "" : ("." + ns.GetHashCode())));
}
同時(shí),如果加載失敗會(huì)觸發(fā)AppDomain.CurrentDomain.AssemblyResolve事件
4. 結(jié)論
1) WebService的序列化是調(diào)用XmlSerializer
2) WebService慢,是因?yàn)楫a(chǎn)生序列化類慢,所謂的臨時(shí)文件都是XmlSerializer的中間代碼。可以在config文件中加入如下的配置,臨時(shí)序列化的文件就不會(huì)被刪除了,WinForm程序是*.exe.config,asp.net是web.config。
<configuration>
<system.diagnostics>
<switches>
<add name="XmlSerialization.Compilation" value="4"/>
</switches>
</system.diagnostics>
</configuration>
臨時(shí)文件在C:\Documents and Settings\抹布\Local Settings\Temp下,注意,因?yàn)槊Q是隨機(jī)的,序列化的dll文件,并不能重用,重開進(jìn)程會(huì)重新生成。
3) 如果自定義序列化類,可以跳過(guò)產(chǎn)生臨時(shí)序列化的步驟,大大提高第一次加載的速度,也就是說(shuō),只要有一個(gè)
程序集名稱+“.XmlSerializers”的序列化類存在,就不會(huì)動(dòng)態(tài)生成序列化程序集了。
4) 在代理類上可以加
[System.Xml.Serialization.XmlSerializerAssemblyAttribute(AssemblyName = "TestPerformance.XmlSerializers")]
指定Xml序列化的類,這個(gè)序列化的類可以通過(guò)一個(gè)工具產(chǎn)生,
但是根據(jù)研究TempAssemlby的LoadGeneratedAssemlby代碼發(fā)現(xiàn),這個(gè)Attribute可以不加的,只要你有一個(gè)GetTempAssemblyName返回值一樣的名稱的序列化類即可。
5) 根據(jù)加載失敗會(huì)觸發(fā)AppDomain.CurrentDomain.AssemblyResolve事件,可以在加載失敗后動(dòng)態(tài)產(chǎn)生序列化類,如下。
private void Form1_Load(object sender, EventArgs e)
{
AppDomain.CurrentDomain.AssemblyResolve +=
new ResolveEventHandler(MyResolveEventHandler);
}
static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
Assembly a = null;
string[] arr = args.Name.Split(new string[] { "." }, StringSplitOptions.None);
if (args.Name.IndexOf("XmlSerializers") >= 0)
{
if (!System.IO.File.Exists(args.Name + ".dll"))
PreGenNS.Pregen.Generate(new string[] { arr[0] });
string sSerializersDLL = args.Name + ".dll";
string smartDeploymentHostLocation = "";
a = Assembly.LoadFrom(smartDeploymentHostLocation + sSerializersDLL);
}
return a;
}
6)VS2005利用Release編譯,會(huì)產(chǎn)生AssemblyName+"XmlSerializer.dll"的序列化文件,可以隨著客戶端一起部署,跟5這種方式不太一樣,可以根據(jù)實(shí)際情況來(lái)選擇。
利用5這種方式,是第一次調(diào)用WebService時(shí),動(dòng)態(tài)生成序列化類;而6是在軟件發(fā)布時(shí),生成這個(gè)類,并部署到客戶端。