客戶(hù)端和服務(wù)之間通過(guò)消息交換(Message Exchange)來(lái)完成方法調用和數據傳遞,WCF 定義了 3 種消息交換模式。
1. Request/Reply這是缺省模式,又被稱(chēng)之為同步調用。在調用服務(wù)方法后需要等待服務(wù)的消息返回,即便該方法返回 void 類(lèi)型。
[ServiceContract]
public interface IContract
{
[OperationContract]
void Test();
}
public class MyService : IContract
{
public void Test()
{
Thread.Sleep(3000);
Console.WriteLine("Test Execute:{0}", DateTime.Now);
}
}
public class WcfTest
{
public static void Test()
{
AppDomain.CreateDomain("Server").DoCallBack(delegate
{
ServiceHost host = new ServiceHost(typeof(MyService));
host.AddServiceEndpoint(typeof(IContract), new BasicHttpBinding(),
"http://localhost:8080/myservice");
host.Open();
});
ChannelFactory<IContract> factory = new ChannelFactory<IContract>(new BasicHttpBinding(),
"http://localhost:8080/myservice");
IContract o = factory.CreateChannel();
Console.WriteLine("Start:{0}", DateTime.Now);
o.Test();
Console.WriteLine("End:{0}", DateTime.Now);
}
}
輸出:
Start:2007-3-27 15:26:07
Test Execute:2007-3-27 15:26:10
End:2007-3-27 15:26:10
2. one-way這種方式在調用方法后會(huì )立即返回,非常類(lèi)似于異步行為。不過(guò)需要注意的是 one-way 不能用在非void,或者包含 out/ref 參數的方法上,會(huì )導致拋出 InvalidOperationException 異常。
[ServiceContract]
public interface IContract
{
[OperationContract(IsOneWay=true)]
void Test();
}
public class MyService : IContract
{
public void Test()
{
Thread.Sleep(3000);
Console.WriteLine("Test Execute:{0}", DateTime.Now);
}
}
public class WcfTest
{
public static void Test()
{
AppDomain.CreateDomain("Server").DoCallBack(delegate
{
ServiceHost host = new ServiceHost(typeof(MyService));
host.AddServiceEndpoint(typeof(IContract), new BasicHttpBinding(),
"http://localhost:8080/myservice");
host.Open();
});
ChannelFactory<IContract> factory = new ChannelFactory<IContract>(
new BasicHttpBinding(), "http://localhost:8080/myservice");
IContract o = factory.CreateChannel();
Console.WriteLine("Start:{0}", DateTime.Now);
o.Test();
Console.WriteLine("End:{0}", DateTime.Now);
}
}
輸出:
Start:2007-3-27 15:27:18
End:2007-3-27 15:27:18
Test Execute:2007-3-27 15:27:21
3. duplex這種模式相對復雜一些,我們詳細描述一下步驟:
(1) 設計標準的服務(wù)契約。為了完成回調操作,我們必須指定 SessionMode 和回調類(lèi)型。
[ServiceContract(SessionMode=SessionMode.Required, CallbackContract=typeof(ICallback))]
public interface IContract
{
[OperationContract]
void Test();
}
(2) 設計回調接口類(lèi)型。由于回調方法在客戶(hù)端執行,因此無(wú)須添加 ServiceContractAttribute。對于回調操作,服務(wù)器無(wú)須獲取其返回信息,因此添加 IsOneWay=true 特性參數。
public interface ICallback
{
[OperationContract(IsOneWay=true)]
void Call(DateTime d);
}
(3) 實(shí)現服務(wù)契約。通過(guò) OperationContext.Current.GetCallbackChannel 可以獲取回調委托,進(jìn)而完成調用。
public class MyService : IContract
{
ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
public void Test()
{
Console.WriteLine("Test AppDomain:{0}", AppDomain.CurrentDomain.FriendlyName);
callback.Call(DateTime.Now);
}
}
(4) 實(shí)現服務(wù)器。注意必須使用支持 Session 的 Binding 類(lèi)型。
ServiceHost host = new ServiceHost(typeof(MyService));
host.AddServiceEndpoint(typeof(IContract), new WSDualHttpBinding(), "http://localhost:8080/myservice");
host.Open();
(5) 創(chuàng )建客戶(hù)端代理對象。利用 Svcutil 或者我們前面講的方法創(chuàng )建客戶(hù)端代理類(lèi)型代碼。(為顯示方便,代碼有所刪減。)
//------------------------------------------------------------------------------
// <auto-generated>
// 此代碼由工具生成。
// 運行庫版本:2.0.50727.42
//
// 對此文件的更改可能會(huì )導致不正確的行為,并且如果
// 重新生成代碼,這些更改將會(huì )丟失。
// </auto-generated>
//------------------------------------------------------------------------------
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[ServiceContractAttribute(ConfigurationName = "IContract", CallbackContract = typeof(IContractCallback), SessionMode = SessionMode.Required)]
public interface IContract
{
[OperationContractAttribute(Action = "...", ReplyAction = "...")]
void Test();
}
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IContractCallback
{
[OperationContractAttribute(IsOneWay = true, Action = "...")]
void Call(System.DateTime d);
}
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IContractChannel : IContract, IClientChannel
{
}
[DebuggerStepThroughAttribute()]
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class ContractClient : DuplexClientBase<IContract>, IContract
{
public void Test()
{
base.Channel.Test();
}
}
(6) 實(shí)現客戶(hù)端。
創(chuàng )建新的客戶(hù)端項目,將生成的代理文件加入??蛻?hù)端除了要創(chuàng )建一個(gè)實(shí)現回調接口的類(lèi)型外,還要為綁定對象指定一個(gè)監聽(tīng)端口,以便服務(wù)器與之聯(lián)系。
class CallBack : IContractCallback
{
public void Call(DateTime d)
{
Console.WriteLine("Call AppDomain:{0}", AppDomain.CurrentDomain.FriendlyName);
Console.WriteLine("Server DateTime:{0}", d);
}
}
[STAThread]
static void Main(string[] args)
{
WSDualHttpBinding binding = new WSDualHttpBinding();
binding.ClientBaseAddress = new Uri("http://localhost:8081/client");
EndpointAddress address = new EndpointAddress("http://localhost:8080/myservice");
ContractClient client = new ContractClient(new InstanceContext(new CallBack()), binding, address);
client.Test();
}
輸出:
Server
--------------
Test AppDomain:Server
Client
--------------
Call AppDomain:Client.vshost.exe
Server DateTime:2007-3-27 17:01:39