調用服務最簡單的方法就是,直接在VS里面添加服務引用,輸入服務的地址即可,無論是普通Web服務,還是WCF服務均可。VS會根據獲取到的元數據,自動生成客戶端代碼。
如果服務的調用量很大,應用廣泛,可以放在IIS上作為一種Web資源使用。但WCF不限於此,它可以在一個進程中運行,或者可以放到Windows服務進程上運行,實則是一種Windows平台的萬能通信技術。
為了裝逼,今天老周將演示如何手動調用WCF,僅通過Channel(通道)就可以調用。其實,在某些時候,手動也有手動的好處,手動調用的話,代碼量不多,也比較靈活。
當然,這個演示僅供參考。
為了順利完成裝逼演示,首先得弄個服務示例,以往常用的是做一個執行加減乘除的服務來測試,今天既然要裝逼,就裝得有創意一點,就來一個計算N次方的吧,比如2的3次方為8。
不知各位是否記得建立WCF的步驟,這么小的程序,就不用IIS來運行了,直接用一個控制台應用程序可以了,簡單大方美觀有層次。
首先要聲明服務協定。
[ServiceContract(Name = "pow_service", Namespace = "http://my")] interface IService { [OperationContract(Name = "pow", Action = "PowAction", ReplyAction = "PowReply")] double Pow(double x, double y); }
協定只是個接口,在服務器端要實現它,但在客戶端不需要知道實現這接口的代碼,只要在客戶端也定義一個這樣的接口,就可以了。當然,如果你不想重復定義協定,你可以把這個接口定義到一個共享的類庫中,最好用可移植的庫,這樣保證平台萬能性。
在服務器和客戶端中分別定義協定接口有一個好處就是可以兩邊不必保持一致,接口名、接口的方法都可以不同,參數名也可以不同,只要參數 的順序、類型、數量,以及返回值的類型相同就可以。
重點是附加在接口上的ServiceContractAttribute,和附加到方法上的OperationContractAttribute特性。服務器和客戶端的接口名字可以不同,只要附加的這些特性的屬性值相同即可。
所以,在Win10 App客戶端,我可以自己聲明這樣的接口:
[ServiceContract(Name = "pow_service", Namespace = "http://my")] interface IPowService { [OperationContract(Name = "pow", Action = "PowAction", ReplyAction = "PowReply")] Task<double> PowAsync(double x, double y); }
你不妨看一下,接口名字和接口成員名字不同,但標注的協定特性是相同的。所謂的協定者,就是服務器和客戶端之間必須有一點“默契”,協定接口就是一種規范,不然,客戶端不知道服務有哪些操作方法,就無法調用了。
協定完成后,在服務器端要實現協定接口,進行具體的操作。
class PowService : IService { public double Pow(double x, double y) { double res = Math.Pow(x, y); return res; } }
承載在進程上的WCF比較好處理,不用太復雜的配置,幾行代碼就可以啟動服務主機。
using (ServiceHost host = new ServiceHost(typeof(PowService))) { NetTcpBinding binding = new NetTcpBinding(SecurityMode.None); // 直接添加終結點 host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://localhost:1700/pow"); host.Opened += (h, ea) => Console.WriteLine("服務已打開。"); // 打開服務 try { host.Open(); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadKey(); }
其實很Easy,ServiceHost負責運行服務,可解釋為服務主機。因為是手動訪問服務,基址可以省略。然后你直接將服務協定相應地作為終結點添加到服務主機中即可。在添加終結點時,需要指明協定的Type,一個Binding,當然還要包含地址。
這里我選用TCP協議來通信,就用NetTcpBinding類,NET TCP的地址要以“net.tcp:”開頭。
之后,直接Open就可以運行服務了。
注意這個服務只能手動調用,不能用服務引用生成代碼,因為沒有公開元數據,生成代碼時會找不到WSDL。
=========================================================
然后在Windows App客戶端中就可以直接調用了。
// 終結點地址 EndpointAddress ep = new EndpointAddress("net.tcp://localhost:1700/pow"); // TCP綁定 NetTcpBinding binding = new NetTcpBinding(SecurityMode.None); // 創建通道 ChannelFactory<WCFSVContracts.IPowService> factory = new ChannelFactory<WCFSVContracts.IPowService>(binding, ep); WCFSVContracts.IPowService channel = factory.CreateChannel(); // 調用服務 double x = double.Parse(txt1.Text); double y = double.Parse(txt2.Text); double r = await channel.PowAsync(x, y); tbres.Text = $"計算結果:{r:G}";
WCFSVContracts.IPowService就是在客戶端上重新定義的服務協定,但協定的特性要與服務相同,接口名字可以不同。這個上面貼過代碼,下面我再貼一段完整的。
using System; using System.ServiceModel; using System.Threading.Tasks; namespace WCFSVContracts { [ServiceContract(Name = "pow_service", Namespace = "http://my")] interface IPowService { [OperationContract(Name = "pow", Action = "PowAction", ReplyAction = "PowReply")] Task<double> PowAsync(double x, double y); } }
與服務器上的定義有些不同的是,在客戶端中,我將操作方法聲明為支持異步等待,即返回Task<TResult>,這個是允許的,大家不必懷疑。
你會看到,在客戶端上調用也是挺簡單的,首先用一個EndpointAddress表示終結點的地址,這個地址必須和服務器上添加的終結點的地址一致,否則找不到服務。
然后實例化一個NetTcpBinding對象,Binding的類型與屬性值必須與服務器上的Binding一致。服務器上的NetTcpBinding的安全模式設置為None,客戶端上的也要設置為None。因為也不是什么見不得人的數據,就禁用安全模式。
最后,用一個ChannelFactory<TChannel>就可以完成服務調用。TChannel的類型就是協定接口,隨着當調用CreateChannel方法時,它所創建的通道就以協定接口為基礎返回,它返回的實際類型是一個動態類型,但它可以以協定接口為基礎來調用。
運行結果如下圖。

