添加Web引用的時候,WebService在客戶端有一個代理,如下:
[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
客戶端調用WebServivce就是通過這個代理類來調用的。
2. 調用WebService方法,客戶端和服務器端通信是Xml,所以代理類跟Xml之間就有序列化和反序列化的過程
3. 客戶端調用WebService的過程如下
a) 客戶端調用代理類Hello world方法
string str = (new Service2.WebService1()).HelloWorld ();
b) 代理類調用基類SoapHttpClientProtocal的Invoke方法
public string HelloWorld() {
object[] results = this.Invoke("HelloWorld", new object[0]);
return ((string)(results[0]));
}
c) SoapHttpClientProtocal進行Soap序列化Soap頭和方法,都是這個類自己做的,但是輸入參數和返回值,是利用的XmlSerializer,輸入參數要序列化,返回值要反序列化。
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中有一句參數序列化的代碼如下
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會緩存臨時程序集,這個程序集作用是序列化和反序列化,如果緩存中沒有會調用TempAssembly產生一個
Static的緩存(就是我們每次調用慢的罪魁禍首):private static TempAssemblyCache cache;
獲取緩存中的程序集:this.tempAssembly = XmlSerializer.cache[defaultNamespace, type];
緩存中沒有就去加載:Assembly assembly1 = TempAssembly.LoadGeneratedAssembly(type, defaultNamespace, out implementation1);
加載沒有就去產生(會生成臨時文件并編譯,很慢):
this.tempAssembly = new TempAssembly(new XmlMapping[] { this.mapping }, assembly1, implementation1);
e) TempAssemlby這個類負責加載以及產生臨時程序集
在LoadGeneratedAssemlby方法中,有一段邏輯,就是默認去加載序列化類,這個類的命名是規則如下
internal static string GetTempAssemblyName(AssemblyName parent, string ns)
{
return (parent.Name + ".XmlSerializers" + (((ns == null) || (ns.Length == 0)) ? "" : ("." + ns.GetHashCode())));
}
同時,如果加載失敗會觸發AppDomain.CurrentDomain.AssemblyResolve事件
4. 結論
1) WebService的序列化是調用XmlSerializer
2) WebService慢,是因為產生序列化類慢,所謂的臨時文件都是XmlSerializer的中間代碼??梢栽赾onfig文件中加入如下的配置,臨時序列化的文件就不會被刪除了,WinForm程序是*.exe.config,asp.net是web.config。
<configuration>
<system.diagnostics>
<switches>
<add name="XmlSerialization.Compilation" value="4"/>
</switches>
</system.diagnostics>
</configuration>
臨時文件在C:\Documents and Settings\抹布\Local Settings\Temp下,注意,因為名稱是隨機的,序列化的dll文件,并不能重用,重開進程會重新生成。
3) 如果自定義序列化類,可以跳過產生臨時序列化的步驟,大大提高第一次加載的速度,也就是說,只要有一個
程序集名稱+“.XmlSerializers”的序列化類存在,就不會動態生成序列化程序集了。
4) 在代理類上可以加
[System.Xml.Serialization.XmlSerializerAssemblyAttribute(AssemblyName = "TestPerformance.XmlSerializers")]
指定Xml序列化的類,這個序列化的類可以通過一個工具產生,
但是根據研究TempAssemlby的LoadGeneratedAssemlby代碼發現,這個Attribute可以不加的,只要你有一個GetTempAssemblyName返回值一樣的名稱的序列化類即可。
5) 根據加載失敗會觸發AppDomain.CurrentDomain.AssemblyResolve事件,可以在加載失敗后動態產生序列化類,如下。
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編譯,會產生AssemblyName+"XmlSerializer.dll"的序列化文件,可以隨著客戶端一起部署,跟5這種方式不太一樣,可以根據實際情況來選擇。
利用5這種方式,是第一次調用WebService時,動態生成序列化類;而6是在軟件發布時,生成這個類,并部署到客戶端。