wcf精通1-15

十五天精通WCF——第一天 三種Binding讓你KO80%的業務

  

  轉眼wcf技術已經出現很多年了,也在.net界混的風生水起,同時.net也是一個高度封裝的框架,作為在wcf食物鏈最頂端的我們所能做的任務已經簡單的不能再簡單了,

再簡單的話馬路上的大媽也能寫wcf了,好了,wcf最基本的概念我們放在后面慢慢分析,下面我們來看看神奇的3個binding如何KO我們實際場景中的80%的業務場景。

?

一:basicHttpBinding

  作為入門第一篇,也就不深入談談basic中的信道棧中那些啥東西了,你只需要知道有ABC三個要素,注意不是姨媽巾哦,如果需要詳細了解,可以觀賞我以前的系列。在

這里我就不多說了,太簡單的東西沒意思,先看個例子簡單感受了,你只需知道的是basic走的是http協議就好了,傳輸消息為soap。

1. 契約

復制代碼
 1 using System.Runtime.Serialization;2 using System.ServiceModel;3 4 namespace MyService5 {6     [ServiceContract]7     public interface IHomeService8     {9         [OperationContract]
10         int GetLength(string name);
11     }
12 }
復制代碼

2. 實現類

復制代碼
 1 using System;2 using System.Messaging;3 using System.Threading;4 5 namespace MyService6 {7     public class HomeService : IHomeService8     {9         public int GetLength(string name)
10         {
11             return name.Length;
12         }
13     }
14 }
復制代碼

3. 服務啟動

復制代碼
 1 using System;2 using System.ServiceModel;3 4 namespace MyService5 {6     class Program7     {8         static void Main(string[] args)9         {
10             using (ServiceHost host = new ServiceHost(typeof(HomeService)))
11             {
12                 try
13                 {
14                     host.Open();
15 
16                     Console.WriteLine("服務開啟!");
17 
18                     Console.Read();
19                 }
20                 catch (Exception e)
21                 {
22                     Console.WriteLine(e.Message);
23                 }
24             }
25         }
26     }
27 }
復制代碼

4. 配置config文件

復制代碼
<?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><bindings><netTcpBinding><binding name="IHomeServiceBinding" /></netTcpBinding></bindings><behaviors><serviceBehaviors><behavior name=""><serviceMetadata httpGetEnabled="true" /><serviceDebug includeExceptionDetailInFaults="true" /></behavior></serviceBehaviors></behaviors><services><service name="MyService.HomeService"><endpoint address="http://127.0.0.1:1920/HomeService" binding="basicHttpBinding" contract="MyService.IHomeService"><identity><dns value="localhost" /></identity></endpoint><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /><host><baseAddresses><add baseAddress="http://127.0.0.1:1920"/></baseAddresses></host></service></services></system.serviceModel>
</configuration>
復制代碼

5. 然后通過 servicehost 啟動服務端

復制代碼
using System;
using System.ServiceModel;namespace MyService
{class Program{static void Main(string[] args){using (ServiceHost host = new ServiceHost(typeof(HomeService))){try{host.Open();Console.WriteLine("服務開啟!");Console.Read();}catch (Exception e){Console.WriteLine(e.Message);}}}}
}
復制代碼

?

好了,到現在為止,服務端全部開啟完畢,接下來我們通過“添加服務引用”,來添加對客戶端的引用

復制代碼
 1 using System;2 3 namespace ConsoleApplication14 {5     class Program6     {7         static void Main(string[] args)8         {9             HomeServiceReference.HomeServiceClient client = new HomeServiceReference.HomeServiceClient();
10 
11             var s = client.GetLength("12345");
12 
13             Console.WriteLine("長度為:{0}", s);
14 
15             Console.Read();
16         }
17     }
18 }
復制代碼

?

麻蛋,就這么簡單,是的,就這樣簡單的五步,基于http的通信就這樣被不小心的完成了,真不好意思。

?

二:netTcpBinding

  有了basic的代碼,現在我們要改成tcp通信,這會通信走的是字節流,很簡單,改一下服務端的config文件就好了,大家也知道這種性能要比basic好。

復制代碼
<?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><behaviors><serviceBehaviors><behavior name="mxbehavior"><serviceMetadata httpGetEnabled="true" /><serviceDebug includeExceptionDetailInFaults="true" /></behavior></serviceBehaviors></behaviors><services><service name="MyService.HomeService" behaviorConfiguration="mxbehavior"><endpoint address="net.tcp://localhost:19200/HomeService" binding="netTcpBinding" contract="MyService.IHomeService"><identity><dns value="localhost" /></identity></endpoint><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/><host><baseAddresses><add baseAddress="http://localhost:1920/HomeService"/></baseAddresses></host></service></services></system.serviceModel>
</configuration>
復制代碼

?

三:netMsmqBinding

  msmq這個玩意,我想大家都清楚,一個物理上的文件,好處呢,你也明白,就是client和service的所有通信都要經過它的手,這樣任何一方出了問題,只要

它在就沒問題了。同樣我們把tcp改成msmq也是非常簡單的,不過要注意,msmqbinding中是不可以讓契約方法有返回值的。所以我們加上isoneway就好了。

復制代碼
using System.Runtime.Serialization;
using System.ServiceModel;namespace MyService
{[ServiceContract]public interface IHomeService{[OperationContract(IsOneWay = true)]void GetLength(string name);}
}
復制代碼

然后我在mmc上新建一個消息隊列,如下:

然后我們再改動以下配置文件

復制代碼
<?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><behaviors><serviceBehaviors><behavior name="mxbehavior"><serviceMetadata httpGetEnabled="true" /><serviceDebug includeExceptionDetailInFaults="true" /></behavior></serviceBehaviors></behaviors><bindings><netMsmqBinding><binding name="msmqbinding"><security mode="None"/></binding></netMsmqBinding></bindings><services><service name="MyService.HomeService" behaviorConfiguration="mxbehavior"><endpoint address="net.msmq://localhost/private/homequeue" binding="netMsmqBinding"contract="MyService.IHomeService" bindingConfiguration="msmqbinding"><identity><dns value="localhost" /></identity></endpoint><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /><host><baseAddresses><add baseAddress="http://localhost:19200/HomeService"/></baseAddresses></host></service></services></system.serviceModel>
</configuration>
復制代碼

?

縱觀上面的三種binding,配置起來何其簡單,底層的各種通訊協議貌似對我來說都是透明的,其實呢???wcf在底層做了何其多的事情,而我卻沒有挖掘。。。

這對碼農里說也是一種悲哀啊。。。出了問題就只能禱告上天。。。下一篇我會開始深入剖析。

隨筆- 197? 文章- 0? 評論- 3407?

十五天精通WCF——第二天 告別煩惱的config配置

?

  經常搞wcf的基友們肯定會知道,當你的應用程序有很多的“服務引用”的時候,是不是有一種瘋狂的感覺。。。從一個環境遷移到另外一個環境,你需要改變的

endpoint會超級tmd的多,簡直就是搞死了人。。。好了,這篇我們來看看如何最小化配置。

?

一:精簡service的config配置

  就像上一篇的代碼一樣,我的service端的config配置如下:

復制代碼
 1 <?xml version="1.0" encoding="utf-8" ?>2 <configuration>3 <system.servicemodel>4 <behaviors>5 <servicebehaviors>6 <behavior name="mxbehavior">7 <servicemetadata httpgetenabled="true" />8 <servicedebug includeexceptiondetailinfaults="true" />9 </behavior>
10 </servicebehaviors>
11 </behaviors>
12 <services>
13 <service name="myservice.homeservice" behaviorconfiguration="mxbehavior">
14 <endpoint address="net.tcp://localhost:1920/homeservice" binding="nettcpbinding" contract="myservice.ihomeservice">
15 <identity>
16 <dns value="localhost" />
17 </identity>
18 </endpoint>
19 <endpoint address="mex" binding="mexhttpbinding" contract="imetadataexchange" />
20 <host>
21 <baseaddresses>
22 <add baseaddress="http://localhost:19200/homeservice"/>
23 </baseaddresses>
24 </host>
25 </service>
26 </services>
27 </system.servicemodel>
28 </configuration>
復制代碼

?

  通過上面的代碼,你應該知道在system.servicemodel下的所有節點都是wcf專屬的節點,所有的節點數據都會被開啟servicehost這個監聽器時捕獲到,下面我可以

通過servicehost這個監聽器的源碼下面找找相關的讀取config節點的代碼。

?

?

通過上面的截圖,你是不是有一種感覺,就是service的底層也是通過代碼動態的讀取config下面的節點來獲取數據,那就意味著我可以直接將代碼寫入到code中,

對吧,這樣我就可以把我認為該配置的東西配置起來,不該配置的東西全部放到代碼里面去,這樣我的靈活性是不是非常的強大。。。。爽吧,說干就干。。。

復制代碼
 1     class Program12     {3         static void Main(string[] args)4         {5             ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://localhost:19200/HomeService"));6 7             host.AddServiceEndpoint(typeof(IHomeService), new NetTcpBinding(), "net.tcp://localhost:1920/HomeService");8 9             //公布元數據
10             host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
11             host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
12 
13             host.Open();
14 
15             Console.WriteLine("服務已經開啟。。。");
16 
17             Console.Read();
18         }
19     }
復制代碼

?

有人就要說了,地址的話肯定不能是寫死的,必須變活,簡單啊,我就僅僅把ip地址配置到config里面去不就完事了,對不對。

<configuration><appSettings><add key ="baseurl" value="http://localhost:19200/HomeService"/><add key ="endpoindurl" value="net.tcp://localhost:1920/HomeService"/></appSettings>
復制代碼
 1   class Program12     {3         static void Main(string[] args)4         {5             ServiceHost host = new ServiceHost(typeof(HomeService), new Uri(ConfigurationManager.AppSettings["baseurl"]));6 7             host.AddServiceEndpoint(typeof(IHomeService), new NetTcpBinding(), ConfigurationManager.AppSettings["endpoindurl"]);8 9             //公布元數據
10             host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
11             host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
12 
13             host.Open();
14 
15             Console.WriteLine("服務已經開啟。。。");
16 
17             Console.Read();
18         }
19     }
復制代碼

?

現在看的話,是不是清楚多了,如果你覺得我的代碼比較累贅,你可以封裝成一個方法,然后就可以動態的配置nettcp,basic,ws*等等對吧。。。好了,說完服

務端,接下來我們看看client端如何避免。

?

二:精簡client的config配置


  就像上一節那樣,如果我用“服務引用”的話,vs會偷偷的用svcutil.exe來給我們生成一個proxy類和一個config文件,proxy類也就是你看到的xxxclient。。。

可惡的是config里面會給我生成一些亂七八糟的東西,如下圖:

復制代碼
 1 <?xml version="1.0" encoding="utf-8" ?>2 <configuration>3 <system.serviceModel>4 <bindings>5 <netTcpBinding>6 <binding name="NetTcpBinding_IHomeService" />7 </netTcpBinding>8 </bindings>9 <client>
10 <endpoint address="net.tcp://localhost:1920/HomeService" binding="netTcpBinding"
11 bindingConfiguration="NetTcpBinding_IHomeService" contract="HomeServiceReference.IHomeService"
12 name="NetTcpBinding_IHomeService">
13 <identity>
14 <dns value="localhost" />
15 </identity>
16 </endpoint>
17 </client>
18 </system.serviceModel>
19 </configuration>
復制代碼

?

同服務器端一樣,如果我用code做掉,是不是非常的爽呢???那可不可以做掉呢? 我們還得看一下proxy的源碼,首先你會看到其實所謂的proxy只是一個繼承

自clientbase的一個類,如下圖。

?

?

上面的兩幅圖,你會發現,最后的proxy類是通過ChannelFactory<TChannel>類來完成助攻的,那話說回來了,既然底層用了ChannelFactory<TChannel>,

那何不我在代碼里面就用ChannelFactory<TChannel>不是更好嗎???這樣config也省了,對吧,說干就干啦。。。

復制代碼
1     static void Main(string[] args)
2         {
3             ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new NetTcpBinding(), "net.tcp://localhost:1920/homeservice");
4 
5             var channel = factory.CreateChannel();
6 
7             var result = channel.GetLength("12345");
8         }
復制代碼

?

好了,代碼就這么簡單,現在是不是感覺自己萌萌大啦~~~

?

十五天精通WCF——第三天 client如何知道server提供的功能清單

   ?通常我們去大保健的時候,都會找姑娘問一下這里能提供什么服務,什么價格,這時候可能姑娘會跟你口述一些服務或者提供一份服務清單,這樣的話大

家就可以做到童嫂無欺,這樣一份活生生的例子,在wcf中同樣是一個道理,只有client了解service能提供哪些功能,client才可以根據server提供的功能進行

消費,那問題來了,service怎么把功能提供給client進行選擇呢???這個就是我這一篇要聊的wsdl(web service description language)。。。

?

一:wsdl

   現在你已經知道了,wsdl就是server提供給client的清單,那下面問題就來了。server是如何提供的呢???你要是比較仔細的話,可能會知道我在上一

篇提到的一個endpoint,如下截圖。

在上面這幅圖中,你可以看到,Homeservice提供了兩個端點,一個是“服務端點“,一個是“元數據端點”。并且你也看到了,元數據的端點地址是

http://192.168.16.16:19200/mex,當client通過svcutil訪問這個地址的時候,就拿到了server能提供的功能清單,然后client就可以根據這些功能生成一

個代理文件,然后的然后,就是你懂得,各種啪啪啪,XXXClient。

?

二:眼見為實

1.見證wsdl

 要想看見wsdl,你只需要通過http://localhost:19200打開服務地址、如下圖:

?

然后點擊:http://localhost:19200/?singleWsdl

?

現在你看到的就是server功能清單,太tmd的重量級了,已經完完全全果體在世人前了,下一小節我們再詳細的分析下。

?

2. 見證client端的XXXclient

  剛才我也說了,當你用vs做“服務引用”的時候,svcutil會根據http://localhost:19200/mex的地址來查看wsdl,然后生成代理,下面我們具體來看一下。

?

?

點擊確定之后,我們就可以看到在?Service References 文件夾下面生成了一個Reference.cs 文件。

?

然后我們打開Reference.cs,就可以看到一個繼承于ClientBase的HomeServiceClient。

?

?

三:詳細分析wsdl文件

  學wcf,你一定要像svcutil一樣能夠看得懂wsdl。

?

1. 首先看下server提供了一個Update操作,參數是一個id,一個Student這個自定義的復雜類型,同時返回也是Student這個

? ? 復雜類型。

復制代碼
1 namespace MyService
2 {
3     [ServiceContract]
4     public interface IHomeService
5     {
6         [OperationContract]
7         Student Update(int id, Student stu);
8     }
9 }
復制代碼

?

?2. wsdl這個xml文件,剛才你也看到了,下面我們一個個節點看看

??<1> portType 和 operation節點

  當你看到下面的截圖后,我想你也能猜的出來,portType就是契約(IHomeService),operation就是契約方法(Update),不過有點意思的是,在operation

下面你看到了一個input,一個output,這個就是所謂的 ”輸入消息“,”輸出消息”,那是什么意思呢??? 也就是說client到server的消息叫做“輸入消息”,server到

client端叫做“輸出消息”,到這里你應該似乎明白了,我C#中的Update方法是有入參和出參的,然而這映射到wsdl中就是兩條消息,input和output,這個也就是經典

的“請求-響應“模式。

?

好了,繼續往下看,在wsdl:input和wsdl:output中分別有一個Action屬性,這個非常有意思,wcf的底層就是通過這個地址來找到對應的方法,比如我們看到的代理

類中的Update方法上面就有這么一段。

?

?<2> message 和 types節點

  繼續往下看的話,你會發現input和output中還有一個message屬性,對應的為IHomeService_Update_InputMessage和IHomeService_Update_OutputMessage,

這個正好是message節點的引用,如下圖:

從這個圖中,你可以看到input和output下面都有一個wsdl:part節點,這個就是表明input和output中需要攜帶的參數,比如element="tns:Update",就引用了

element中Name=Update的節點,如下圖:

?

好了,最后我再截一張圖,可以看到,傳輸協議為soap,服務地址等等。。。然后就沒什么好說的了。

?

?

十五天精通WCF——第四天 你一定要明白的通信單元Message

  轉眼你已經學了三天的wcf了,是不是很好奇wcf在傳輸層上面到底傳遞的是個什么鳥毛東西呢???應該有人知道是soap,那soap這叼毛長得是什么

樣呢?這一篇我們來揭開答案。。。

?

一:soap到底長成什么樣子

  為了能看清soap長的啥樣,我可以用強大的Fiddler來監視一下,突然好激動啊!!!

1.Server

復制代碼
 1         static void Main(string[] args)2         {3             ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:19200"));4 5             host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie");6 7             //公布元數據8             host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });9 
10             host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
11 
12             host.Open();
13 
14             Console.WriteLine("服務已經開啟。。。");
15 
16             Console.Read();
17         }
復制代碼

2.Client

1             ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://192.168.1.105:19200/HomeServie"));
2 
3             var client = factory.CreateChannel();
4 
5             client.Update("王八蛋");

?

?

現在我想你大概看清楚了這玩意是個么樣子,一個建立在xml上面的一種消息格式,根元素是envelope,我知道這叼毛翻譯過來就是“信封”,所以就有了”封頭“

和”封體”,就是s:Header 和 s:Body,從這個soap中你可以看到它忽略了header,然后我們繼續往下看,還記得Update的意思嗎???如果你讀懂了上一篇,

你應該知道這是一個Action,也就是所謂的input消息。與之對應的就是UpdateResponse這個output消息,對吧,還記得xmlns="http://tempuri.org/">嗎?

它就是IHomeService的默認命名空間,對吧。。。

下一個我們關注的是Update這個Action中的<str>這個,你也看得到,這個就是上圖中Update方法中的str參數,最后我們來看一下UpdateResponse中

的<UpdateResult xmlns:a="http://schemas.datacontract.org/2004/07/MyService,不知道你是否還記得它就是WSDL中關于Student的XSD結

構,看下圖:

?

好了,wcf中的soap結構我們也大概了解了一下,不知道有沒有引發你對soap更深入的思考呢???

?

二:對soap的更深入思考

  通過fiddler觀察,你應該也明白了,不管是客戶端還是服務端,wcf的高層封裝都是僅僅拿出了Envelope中的body節點,而其他節點對我們來說好像并

沒有什么卵用,比如我說的Header節點,這么說來,Header是不是有點浪費呢???那下面有一個問題來了,wcf在底層用什么來構造消息的呢???下面

我們大概找下client端的源碼。。。

?

通過上面的圖,你現在應該也知道了在.net中其實tmd的就是message構造的,所以我想告訴你的是:既然wcf在底層也是用message來構造的,何不我自己

就來構造message消息呢???豈不美哉???這樣我就可以隨意操作message,對吧。。。不然wcf這個高層封裝的叼毛,對我來說就是一種束縛。。。因

為我已經知道了service公布的wsdl,所以我可以輕松構造message。。。

?

三:用message來調用Server端

? 廢話不多說,構造message你一定要知道下圖中的三點:(input這個Action,契約方式 和 服務地址)。

?

好了,下面我先來構造數據契約,指定服務契約的命名空間 和 Action在Soap中的名稱

1     [DataContract(Namespace = "http://tempuri.org/", Name = "Update")]
2     class Test
3     {
4         [DataMember]
5         public string str { get; set; }
6     }

然后,我把這個數據契約塞到envelope中的body中,如下:

復制代碼
 1             BasicHttpBinding bingding = new BasicHttpBinding();2 3             BindingParameterCollection param = new BindingParameterCollection();4 5             var u = new Test() { str = "王八蛋" };6 7             Message request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IHomeService/Update", u);8 9             IChannelFactory<IRequestChannel> factory = bingding.BuildChannelFactory<IRequestChannel>(param);
10 
11             factory.Open();
12 
13             IRequestChannel channel = factory.CreateChannel(new EndpointAddress("http://192.168.1.105:19200/HomeServie"));
14 
15             channel.Open();
16 
17             var result = channel.Request(request);
18 
19             channel.Close();
20 
21             factory.Close();
復制代碼

接下來,我們跑起來看一下,效果咋樣。。。

?

看沒看到,這個就是我手工構造的Message,是不是太帥了。。。哈哈,太帥的應該在后面,剛才也說了,既然大家玩的都是Message,而你這個幾把wcf卻僅僅把

我的message.body拿出來了,那干脆我直接在契約方法中加message豈不是更好么???自由操作Message還有個什么好處呢??當然啦,我可以在Message的

Header中加一些參數token,client的ip地址,client的身份,client的時間等等這些統計信息,對吧。。。這樣才是最帥的,好了,說干就干,我們修改下server端的

契約方法,只用來接受Message。

?

server端:

復制代碼
 1     public class HomeService : IHomeService2     {3         public Message Update(Message message)4         {5             var header = message.Headers;6 7             var ip = header.GetHeader<string>("ip", string.Empty);8 9             var currentTime = header.GetHeader<string>("currenttime", string.Empty);
10 
11             //這個就是牛逼的 統計信息。。。
12             Console.WriteLine("客戶端的IP=" + ip + " 當前時間=" + currentTime);
13 
14             return Message.CreateMessage(message.Version, message.Headers.Action + "Response", "等我吃完肯德基,再打死你這個傻逼!!!");
15         }
16     }
復制代碼

?

client端:

復制代碼
 1 namespace ConsoleApplication12 {3     class Program4     {5         static void Main(string[] args)6         {7             BasicHttpBinding bingding = new BasicHttpBinding();8 9             BindingParameterCollection param = new BindingParameterCollection();
10 
11             var u = new Test() { str = "王八蛋" };
12 
13             Message request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IHomeService/Update", u);
14 
15             //在header中追加ip信息
16             request.Headers.Add(MessageHeader.CreateHeader("ip", string.Empty, Dns.GetHostByName(Dns.GetHostName()).AddressList[0].ToString()));
17             request.Headers.Add(MessageHeader.CreateHeader("currenttime", string.Empty, DateTime.Now));
18 
19             IChannelFactory<IRequestChannel> factory = bingding.BuildChannelFactory<IRequestChannel>(param);
20 
21             factory.Open();
22 
23             IRequestChannel channel = factory.CreateChannel(new EndpointAddress("http://192.168.1.105:19200/HomeServie"));
24 
25             channel.Open();
26 
27             var result = channel.Request(request);
28 
29             channel.Close();
30 
31             factory.Close();
32         }
33     }
34 
35     [DataContract(Namespace = "http://tempuri.org/", Name = "Update")]
36     class Test
37     {
38         [DataMember]
39         public string str { get; set; }
40     }
41 }
復制代碼

?

然后我們用Fiddler監視一下結果:

?

現在一切都如我所愿,好了,我想你也大概明白了這個神奇的message,也不要忘了它就是wcf的基本通信單元,我要去吃肯德基了。。。。。。

?

十五天精通WCF——第五天 你需要了解的三個小技巧

?一: 服務是端點的集合

  當你在開發wcf的時候,你或許已經注意到了一個service可以公布多個endpoint,確實是這樣,在wcf中有一句很經典的話,叫做“服務是端點的集合",就

比如說一個普普通通的服務,它就公布了一個服務端點,一個元數據端點,對吧。。。

仔細一想,這個問題就好玩了,既然一個service可以公布多個endpoint,而且我還知道wcf中有很多的binding,這些binding對應著很多的傳輸方式,那是不是

說我一個service可以用多種協議方法對外公布,比如說同時以nettcp,basic,msmqbinding,udp等方式公布,對吧,那這樣的話是不是超級好玩,如果對方

是非.net程序,那就可以調用我的basic,如果對方是.net程序,那是不是可以調用我的nettcp,對不對。。。當然啦,wcf無所不能,這是一個史上無比強大的牛

逼框架,牛逼的要死,已經逼得程序員只需隨便改幾個配置就能達到完全不一樣的效果。。。下面我同時用nettcp和basic的方式來同時公布服務,好了,現在我

們就來見證奇跡吧。。。

Service:

復制代碼
 1 using System;2 using System.Runtime.Serialization;3 using System.ServiceModel;4 using System.ServiceModel.Channels;5 using System.Threading;6 7 namespace MyService8 {9     public class HomeService : IHomeService
10     {
11         public Student Update(Student message)
12         {
13             return new Student() { Name = "一線碼農" };
14         }
15     }
16 
17     [DataContract]
18     public class Student
19     {
20         [DataMember]
21         public string Name { get; set; }
22 
23         [DataMember]
24         public int Age { get; set; }
25     }
26 }
復制代碼

Host :

復制代碼
 1     class Program12     {3         static void Main(string[] args)4         {5             ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:1920"));6 7             host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie");8 9             host.AddServiceEndpoint(typeof(IHomeService), new NetTcpBinding(), "net.tcp://192.168.1.105:1921/HomeServieTcp");
10 
11             host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
12 
13             host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
14 
15             host.Open();
16 
17             Console.Read();
18         }
19     }
復制代碼

Client端:

復制代碼
 1     class Program2     {3         static void Main(string[] args)4         {5             //basic 方式6             ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(),7                                                                                     new EndpointAddress("http://192.168.1.105:1920/HomeServie"));8 9             var client = factory.CreateChannel();
10 
11             var result = client.Update(new Student() { });
12 
13 
14             //nettcp方式
15             factory = new ChannelFactory<IHomeService>(new NetTcpBinding(),
16                                                                        new EndpointAddress("net.tcp://192.168.1.105:1921/HomeServieTcp"));
17 
18             client = factory.CreateChannel();
19 
20             result = client.Update(new Student() { });
21         }
22     }
復制代碼

?

通過上面的代碼,是不是已經發現,我在client端,既可以用basic的方式調用,又可以用nettcp的方式調用,這個技巧是不是感覺wcf無比強大呢???

?

二:Host寄宿多個Service

  我們知道wcf的寄宿方式有很多種,有iis,有windowservice,還有簡單方便的console方式,而默認情況下,我們最通常的方法都是一個service,一個寄宿,

而其實呢??? 其實一個寄宿host可以承載多個service,看起來是不是很好玩,如果說你有10個servcie,現在你只需要用一個console host就能寄宿起來,廢

話不多說,我演示一下給你看就好了。

Service:

復制代碼
 1     namespace MyService2     {3         [ServiceContract]4         public interface IHomeService5         {6             [OperationContract]7             Student Update(Student message);8         }9 
10         [ServiceContract]
11         public interface IFlyService
12         {
13             [OperationContract]
14             Student Fly(Student stu);
15         }
16     }
復制代碼

Host:

復制代碼
 1     class Program12     {3         static void Main(string[] args)4         {5             //第一個: 這是Home服務6             ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:1920"));7             host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie");8             host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });9             host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
10             host.Open();
11 
12             Console.WriteLine("Home服務開啟。。。。");
13 
14             //第一個: 這是Fly服務
15             var host2 = new ServiceHost(typeof(FlyService), new Uri("http://192.168.1.105:1930"));
16             host2.AddServiceEndpoint(typeof(IFlyService), new BasicHttpBinding(), "FlyServie");
17             host2.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
18             host2.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
19             host2.Open();
20 
21             Console.WriteLine("Fly服務開啟。。。。");
22 
23             Console.Read();
24         }
25     }
復制代碼

有沒有看到,現在兩個服務都開啟了,這種方式看起來是不是很爽呀,否則的話,你需要開啟兩個Host,這樣的話,我的手續就精簡了。。。對吧。。

?

三: Tcp中的端口共享

   這玩意聽起來大家都懂,端口共享嘛,不就是兩個程序共享一個端口,對吧,在通常情況下,我們肯定會認為這無法做到,其實呢?在Wcf中我們還是可以玩

的,也就是一個PortSharingEnabled的事!!!如果說端口可以共享的話,那我們的service是不是就可以少開辟幾個端口呢?同樣這也方便我們進行service的管

理,下面我給大家繼續演示一下。。。很好玩的,么么噠

?

可以看到,我的兩個host都是用1920的端口,并且現在我真的開啟起來啦。。。。好了,三種技巧都說到了,我想你在現實的wcf開發中,或多或少的都能接

觸的到,希望對你有用~~~~

?

隨筆- 197? 文章- 0? 評論- 3407?

十五天精通WCF——第六天 你必須要了解的3種通信模式

  

? ? ?wcf已經說到第六天了,居然還沒有說到這玩意有幾種通信模式,慚愧慚愧,不過很簡單啦,單向,請求-響應,雙工模式,其中的第二種“請求-響應“

模式,這個大家不用動腦子都清楚,這一篇我大概來分析下。

?

一:“請求-響應“模式

  ? 如果你看了我上一篇的博文,你應該非常清楚這種類似“本地調用”的方式,wcf同樣也分為“同步”和“異步”兩種,不過不管是異步還是同步,最終都逃

不過是“請求-響應”這個事實,對吧。

?

1: 同步方式

  這種方式我想沒什么好說的,前面幾篇我已經說的非常清楚了,具體使用方法可以參考我的前面幾篇文章。。。謝啦~~~~

?

2: 異步方式

  通常我們都有這樣的一個思維,遇到耗時的東西第一反應就想到了多線程,畢竟多線程也是一種負載均衡,在wcf這種”請求-響應“模式,同樣也支持異

步,很神奇吧,而且神奇到可以在“服務引用“界面上做到一鍵生成,什么???你不信!!!!不信你看。。。

?

然后我非常好奇的看下XXXClient給我們生成的是個什么代碼。。。

?

通過client端的proxy代碼,你可以清楚的看到,這雞巴WCF真的不容易,給我們生成了兩種“異步模式”,第一種是最古老的beginXXX,endXXX模式,

還有一種是被Jeffrey Richter 嚴重鄙視的“事件異步模式”。。。沒什么好說的,截圖一下給大家看看。

?

二:“單向“模式

  很多時候,我們或許都有這樣的需求,比如說訂單提交成功的時候,我需要給客戶發送郵件,但是你想想,我發送郵件這個任務只是我訂單流程的

一個“額外任務“,也就是說,它的失敗不應該會阻止我的訂單流程,并且它的邏輯時間不應該會阻礙我的下單總時間,對吧。。。這樣的話,我的訂單時

間才會最小化,為了達到不影響下單總時間的效果,我的想法就是,client端直接把消息丟給信道就好了,然后不管server端有沒有真的接收到,處理的

慢不慢,過的好不好,等等,非常開心的是,這些對wcf來說真的是小菜一碟,只需要一個輕輕松松的”IsOneWay=true“屬性就可以了。。。牛逼的要

死。。。還有就是因為是單向的,所以契約方法就沒有存在返回值的必要了,我說的對吧。。。嘿嘿~~~

1     [ServiceContract]
2     public interface IHomeService
3     {
4         [OperationContract(IsOneWay = true)]
5         void Update(Student message);
6     }
復制代碼
 1 namespace MyService2 {3     public class HomeService : IHomeService4     {5         public void Update(Student message)6         {7             Console.WriteLine(message.Name);8         }9     }
10 
11     [DataContract]
12     public class Student
13     {
14         [DataMember]
15         public string Name { get; set; }
16 
17         [DataMember]
18         public int Age { get; set; }
19     }
20 }
復制代碼

為了驗證是否真的是單向通訊,我可以用二種方法驗證下。

?

1. wsdl中是否有output這個message

  通過下面的圖,我想你看的很清楚了,你再也沒有找到我們熟悉的“output”這個message,這就說明貌似真的是單向的了,因為wsdl就是web服務的清單。

  

?

2. 使用fillder監視一下請求消息

復制代碼
1     class Program
2     {
3         static void Main(string[] args)
4         {
5             HomeServiceClient client = new HomeServiceClient();
6 
7             client.Update(new Student() { Name = "hxc" });
8         }
9     }
復制代碼

  

正如我在圖中說的那樣,非常奇怪,我的IsOneWay模式,竟然在http模式下行不通,但是你要記住,http模式天生就是“請求-響應”模式,它完全做不了

單向模式,說明白一點就是:“wcf發現你的bingding不支持單向“的時候,它并不會報錯,還是用自己天生的”請求-相應“模式來模擬”單向通信“,這就是你

看到的非常奇怪的Http 202這個http狀態碼,很多人包括我,都不知道http202 是幾個意思,沒關系,我們百科一下就好了。。。下面框框的里面的字,

已經說的非常清楚了,感謝感謝。。。

?

三:“雙向“ 模式

  這個通訊其實沒什么好講的,也只有tcp模式才會天生支持,而http模式天生就不支持,就像上面一樣,如果非要用http來支持“雙向通訊“,那又是在

坑"wcf"他爹,這樣就會逼著他爹在底層再建立一個“請求-響應“模式來支持所謂的”雙向通訊“,而且”雙向通訊“這個玩意還不如用兩個單向的”請求-響應”模

式或者兩個“單向模式”來支持,而且兩個”請求-響應“模式比”雙向通訊“有更大的靈活性,反正我是對它不感冒,了解一下即可,如果大家比較感興趣,可以

在wcf官網上看一下:https://msdn.microsoft.com/zh-cn/library/ms735119.aspx。

 

? 好了,就說到這里,洗洗睡了,晚安~~~~

?

隨筆- 197? 文章- 0? 評論- 3407?

十五天精通WCF——第七天 Close和Abort到底該怎么用才對得起觀眾

?

一:文起緣由

? ? ? ? ? 寫這一篇的目的源自于最近看同事在寫wcf的時候,用特別感覺繁瑣而且云里霧里的嵌套try catch來防止client拋出異常,特別感覺奇怪,就比如下面的代碼。

復制代碼
 1         public void StartNormalMarketing(int shopId, List<int> marketingIdList)2         {3 4             using (SendEventMarketingService.DistributeServiceClient client = new SendEventMarketingService.DistributeServiceClient())5             {6                 try7                 {8 9                     client.StartByMarketingIDList(shopId, marketingIdList, SendEventMarketingService.MarketingType.NormalMarketing);
10 
11                 }
12                 catch (Exception ex)
13                 {
14                     LogHelper.WriteLog("常規營銷活動開啟服務", ex);
15                 }
16                 finally
17                 {
18                     try
19                     {
20                         client.Close();
21                     }
22                     catch (Exception)
23                     {
24                         client.Abort();
25                     }
26                 }
27             }
28         }
復制代碼

看完上面的代碼,不知道你是否有什么感想?而且我還問了同事,為什么try catch要寫成這樣,同事說是根據什么書上來的什么最佳實踐,這話一說,我也不敢輕易

懷疑了,只能翻翻源代碼看看這話是否有道理,首先我來說說對這段代碼的第一感覺。。。

?

1. 代碼特別繁瑣

  我們寫代碼,特別不喜歡繁瑣,上面的代碼就是一例,你try catch就try catch,還在finally中嵌套一個try catch,真的有點感覺像吃了兩只癩蛤蟆一樣。。。

?

2. 混淆close和abort的用法  

  這種代碼給人的感覺就是為什么不精簡一下呢???比如下面這樣,起碼還可以少寫一對try catch,對吧。

復制代碼
 1         public void StartNormalMarketing(int shopId, List<int> marketingIdList)2         {3 4             using (SendEventMarketingService.DistributeServiceClient client = new SendEventMarketingService.DistributeServiceClient())5             {6                 try7                 {8 9                     client.StartByMarketingIDList(shopId, marketingIdList, SendEventMarketingService.MarketingType.NormalMarketing);
10 
11                     client.Close();
12                 }
13                 catch (Exception ex)
14                 {
15                     LogHelper.WriteLog("常規營銷活動開啟服務", ex);
16 
17                     client.Abort();
18                 }
19             }
20         }
復制代碼

而且乍一看這段代碼和文中開頭那一段代碼貌似實現一樣,但是某些人的“最佳實踐”卻不是這樣,所以確實會導致我這樣的后來人犯迷糊,對吧。。。反正我就是頭暈,

簡直就是弄糊涂到什么時候該用close,什么時候該用abort。。。

      

二:探索原理

  為了弄明白到底可不可以用一個try catch來替代之,下面我們一起研究一下。

?

1. ?從代碼注釋角度甄別

    從類庫的注釋中,可以比較有意思的看出,abort方法僅僅比close多一個“立即”,再無其他,有意思,不過這對我來說并沒有什么卵用,因為這個注釋太

籠統了,為了讓自己更加徹底的明白,只能來翻看下close和abort的源代碼。

?

2. ?從源碼角度甄別

  為了方便讓ILSpy調試Client代碼,現在我決定用ChannelFactory來代替,如下圖:

復制代碼
 1 namespace ConsoleApplication12 {3     class Program4     {5         static void Main(string[] args)6         {7             ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>();8 9             try
10             {
11                 var channel = factory.CreateChannel();
12 
13                 factory.Close();
14             }
15             catch (Exception ex)
16             {
17                 factory.Abort();
18             }
19         }
20     }
21 }
復制代碼

為了讓大家更好的理解,我把close方法的源碼提供如下:

復制代碼
 1 // System.ServiceModel.Channels.CommunicationObject2 [__DynamicallyInvokable]3 public void Close(TimeSpan timeout)4 {5     if (timeout < TimeSpan.Zero)6     {7         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("timeout", SR.GetString("SFxTimeoutOutOfRange0")));8     }9     using ((DiagnosticUtility.ShouldUseActivity && this.TraceOpenAndClose) ? this.CreateCloseActivity() : null)
10     {
11         CommunicationState communicationState;
12         lock (this.ThisLock)
13         {
14             communicationState = this.state;
15             if (communicationState != CommunicationState.Closed)
16             {
17                 this.state = CommunicationState.Closing;
18             }
19             this.closeCalled = true;
20         }
21         switch (communicationState)
22         {
23         case CommunicationState.Created:
24         case CommunicationState.Opening:
25         case CommunicationState.Faulted:
26             this.Abort();
27             if (communicationState == CommunicationState.Faulted)
28             {
29                 throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
30             }
31             goto IL_174;
32         case CommunicationState.Opened:
33         {
34             bool flag2 = true;
35             try
36             {
37                 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
38                 this.OnClosing();
39                 if (!this.onClosingCalled)
40                 {
41                     throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);
42                 }
43                 this.OnClose(timeoutHelper.RemainingTime());
44                 this.OnClosed();
45                 if (!this.onClosedCalled)
46                 {
47                     throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this);
48                 }
49                 flag2 = false;
50                 goto IL_174;
51             }
52             finally
53             {
54                 if (flag2)
55                 {
56                     if (DiagnosticUtility.ShouldTraceWarning)
57                     {
58                         TraceUtility.TraceEvent(TraceEventType.Warning, 524292, SR.GetString("TraceCodeCommunicationObjectCloseFailed", new object[]
59                         {
60                             this.GetCommunicationObjectType().ToString()
61                         }), this);
62                     }
63                     this.Abort();
64                 }
65             }
66             break;
67         }
68         case CommunicationState.Closing:
69         case CommunicationState.Closed:
70             goto IL_174;
71         }
72         throw Fx.AssertAndThrow("CommunicationObject.BeginClose: Unknown CommunicationState");
73         IL_174:;
74     }
75 }
復制代碼

然后我提供一下Abort代碼:

復制代碼
 1 // System.ServiceModel.Channels.CommunicationObject2 [__DynamicallyInvokable]3 public void Abort()4 {5     lock (this.ThisLock)6     {7         if (this.aborted || this.state == CommunicationState.Closed)8         {9             return;
10         }
11         this.aborted = true;
12         this.state = CommunicationState.Closing;
13     }
14     if (DiagnosticUtility.ShouldTraceInformation)
15     {
16         TraceUtility.TraceEvent(TraceEventType.Information, 524290, SR.GetString("TraceCodeCommunicationObjectAborted", new object[]
17         {
18             TraceUtility.CreateSourceString(this)
19         }), this);
20     }
21     bool flag2 = true;
22     try
23     {
24         this.OnClosing();
25         if (!this.onClosingCalled)
26         {
27             throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);
28         }
29         this.OnAbort();
30         this.OnClosed();
31         if (!this.onClosedCalled)
32         {
33             throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this);
34         }
35         flag2 = false;
36     }
37     finally
38     {
39         if (flag2 && DiagnosticUtility.ShouldTraceWarning)
40         {
41             TraceUtility.TraceEvent(TraceEventType.Warning, 524291, SR.GetString("TraceCodeCommunicationObjectAbortFailed", new object[]
42             {
43                 this.GetCommunicationObjectType().ToString()
44             }), this);
45         }
46     }
47 }
復制代碼

?

仔細觀察完這兩個方法,你會發現什么呢???至少我可以提出下面四個問題:

?

1:Abort是Close的子集嗎?

  ?是的,因為如果你看懂了Close,你會發現Close只針對Faulted 和Opened做了判斷,而其中在Faulted的枚舉下會調用原生的Abort方法。。。如下圖

?

2:我能監視Client的各種狀態嗎?比如Created,Opening,Fault,Closed等等。。。

   當然可以了,wcf的信道老祖宗就是ICommunicationObject,而它就有5種監聽事件,這些就可以隨時監聽,懂伐???

復制代碼
 1         static void Main(string[] args)2         {3             ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie"));4 5             try6             {7                 factory.Opened += (o, e) =>8                 {9                     Console.WriteLine("Opened");
10                 };
11 
12                 factory.Closing += (o, e) =>
13                 {
14                     Console.WriteLine("Closing");
15                 };
16 
17                 factory.Closed += (o, e) =>
18                 {
19                     Console.WriteLine("Closed");
20                 };
21 
22                 var channel = factory.CreateChannel();
23 
24                 var result = channel.Update(new Student() { });
25 
26                 factory.Close();
27             }
28             catch (Exception ex)
29             {
30                 factory.Abort();
31             }
32         }
復制代碼

?

3:Abort會拋出異常嗎?

  

從這個截圖中可以看到非常有意思的一段,那就是居然abort活生生的把異常給吞了。。。骨頭都不給吐出來。。。真tmd的神奇到家了,想想也有道理,因為只有

這樣,我們上層的代碼在catch中才不會二次拋出“未處理異常”了,對吧,再轉念看一下Close方法。

?

從上面圖中可以看到,Close在遇到Faulted之后調用Abort方法,如果說Abort方法調用失敗,Close方法會再次判斷狀態,如果還是Faulted的話,就會向上拋出

異常。。。這就是為什么Abort不會拋異常,Close會的原因,所以Close千萬不要放在Catch塊中。

?

4. Abort代碼大概都干了些什么

  這個問題問的好,要能完美解決的話,我們看下代碼,如下圖,從圖中可以看到,Abort的大目的就是用來關閉信道,具體會經過closeing,abort和closed這

三個方法,同時,這三個事件也會被老祖宗ICommunicationObject監聽的到。

?

?

好了,最后我們關注的一個問題在于下面這條語句是否應該放在Try塊中???

1  ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie"));

很簡單,我們簡要的看一下代碼,看里面是否會有“異常”拋出即可。。。。

?

可以看到,在new的過程中可能,或許會有異常的產生,所以最好把try catch改成下面這樣。。。

復制代碼
 1     class Program2     {3         static void Main(string[] args)4         {5             ChannelFactory<IHomeService> factory = null;6             try7             {8                 factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie"));9 
10                 var channel = factory.CreateChannel();
11 
12                 var result = channel.Update(new Student() { });
13 
14                 factory.Close();
15 
16                 throw new Exception();
17             }
18             catch (Exception ex)
19             {
20                 if (factory != null)
21                     factory.Abort();
22             }
23         }
24     }
復制代碼

?

好了,綜合我上面所說的一切,我個人覺得最好的方式應該是上面這樣,夜深了,睡覺了,晚安。

?

隨筆- 197? 文章- 0? 評論- 3407?

十五天精通WCF——第八天 對“綁定”的最后一點理解

  

  轉眼已經中斷10幾天沒有寫博客了,也不是工作太忙,正好碰到了端午節,然后最近看天津臺的愛情保衛戰入迷了。。。太好看了,一直都是回味無窮。。。而且

涂磊老師話說的真是tmd的經典,然后就這樣耽擱了,好了,話不多說,這篇我們看看binding中最后一點需要知道的東西。

?

一:信道棧

  我在之前的文章中多次提到信道棧,不知道大家對它的概念是否有了解,其實想想也還是蠻簡單的,既然是棧,那么這個棧肯定就不止一個元素了,對吧,第二個

的話,既然是棧,那么肯定就遵循FILO的原則,可能你會說,這個還是蠻抽象的,能給個具體的例子么???恭喜你,wcf中還真有一個方法CreateBindingElements,

下面我們具體看看。。。

?

1. ?簡單看看各種binding的棧中都有些什么

  

看到上面的監控窗口,是不是有點意思,在BasicHttpBinding的信道棧中有兩個元素,分別是HttpTransportBindingElement和TextMessageEncodingBindingEl

ement,通過名字也能很容易的判斷出來,一個是“http傳輸協議”,一個是“文本消息編碼協議”,然后再看看復雜一點的WSHttpBinding,你會發現,他不光有Basic

的所有東西,還包括SymmetricSecurityBindingElement(安全協議) 和?TransactionFlowBindingElement(事務流),現在你心中是不是有底了,起碼我知道各

種Binding里面都有些啥,為了更好的理解,我來畫一張簡圖。

上面這個圖,大概也就表達了我的意思,當我們Client在走WSHttpBinding這個協議的時候,Client端的InputMessage會先走?TransactionFlow,SymmetricSec

urity,TextMessageEncoding,最后走HttpTransport,然后Service端就按照客戶端進行“反向處理”,通過一陣禁臠之后,我們就拿到了安全的OutputMessage。

?

二:BindingElement的跨綁定性

  你要是很仔細的話,你肯定會發現,其實Binding就是一個預先默認配置好的信道棧,對不對,你也看到了,每一種Binding都有屬于自己的BindingElements,

恰恰這些Elements是可以跨Binding的,也就是說我可以自由組合Elements,這樣是不是可以給我們這些寒酸的碼農最大的靈活性,對吧,舉個簡單的例子,

BasicHttpBinding有兩個綁定元素,其中對soap消息進行的是TextMessageEncoding編碼對吧,而netTcpBinding對soap進行的BinaryMessageEncoding,

然后你也應該知道了,我想做一個自定義的Binding,其中消息編碼是BinaryMessage,傳輸協議是HttpTransport,那怎么做呢????

Host文件:

復制代碼
 1     class Program12     {3         static void Main(string[] args)4         {5             ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:1920"));6 7             var customBinding = new CustomBinding();8 9             customBinding.Elements.Add(new BinaryMessageEncodingBindingElement());
10             customBinding.Elements.Add(new HttpTransportBindingElement());
11 
12             host.AddServiceEndpoint(typeof(IHomeService), customBinding, "HomeServie");
13 
14             host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
15 
16             host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
17 
18             host.Open();
19 
20             Console.WriteLine("服務已經開啟!!!");
21 
22             Console.Read();
23         }
24     }
復制代碼

?

Client調用:

復制代碼
 1     static void Main(string[] args)2         {3             ServiceReference1.HomeServiceClient client = new ServiceReference1.HomeServiceClient();4 5             var result = client.Update("你好");6 7             Console.WriteLine("server value:" + result);8 9             Console.Read();
10         }
復制代碼

最后我們用Fiddler監視一下,最后我們看看,都是些亂碼。

?

這篇就說到這里了,希望對你有幫助,下一篇我們看看WCF中的Behavior,很好玩的哦~~~

?

隨筆- 197? 文章- 0? 評論- 3407?

十五天精通WCF——第九天 高級玩法之自定義Behavior

?

  終于我又看完了二期愛情保衛戰,太酸爽了,推薦鏈接:http://www.iqiyi.com/a_19rrgublqh.html?vfm=2008_aldbd,不多說,誰看誰入迷,下面言歸正傳,

看看這個很有意思的Behavior。

?

一: Behavior這個潑婦的厲害

  ? 在前面的文章中,我也清楚的說明了整個wcf通信流,而Behavior這個潑婦可以在wcf通信流中的任何地方插上一腳,蠻狠無比,利用的好,讓你上天堂,利用的不

好,讓你下地獄。。。下面讓你看看behavior到底有哪些可以注入的點???先畫個簡圖:

上面的圖,大概就是wcf的通信簡圖,所有藍色字體都是Behavior注入的點,其中Client和Service端都可以注入,如果按照功能分的話,又可以分為“操作級別”和

”端點級別“,下面我來簡要的分解下。

?

二:端點級別Behavior

  從圖中你也可以看到,消息檢查器是放在Channel這個級別的,也就是說它可以監視Client和Server的入站請求,也就是說所有的請求都需要通過它轉發,如果

這樣的話,那我是不是可以在這個注入點上自由的修改,變更,攔截入站和出站請求,而且利用這個特性我還可以做很多的事情,比如日志記錄,記錄統計等等,下

面我們來看看這個怎么使用??? 只需要extends?IEndpointBehavior ?和?IDispatchMessageInspector,然后加入EndpointBehaviors即可。。。

?1.?IDispatchMessageInspector

復制代碼
 1     public class MyDispatchMessageInspector : IDispatchMessageInspector2     {3         public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)4         {5             Console.WriteLine(request.ToString());6             return request;7         }8 9         public void BeforeSendReply(ref Message reply, object correlationState)
10         {
11             Console.WriteLine(reply.ToString());
12         }
13     }
復制代碼

2.?IEndpointBehavior

復制代碼
 1     public class MyEndpointBehavior : IEndpointBehavior2     {3         public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)4         {5         }6 7         public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)8         {9         }
10 
11         public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
12         {
13             endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyDispatchMessageInspector());
14         }
15 
16         public void Validate(ServiceEndpoint endpoint)
17         {
18         }
19     }
復制代碼

3. 將MyEndpointBehavior加入到Host中

復制代碼
 1   static void Main(string[] args)2         {3             ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://127.0.0.1:1920"));4 5             host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie");6 7             host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });8 9             host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
10 
11             host.Description.Endpoints[0].EndpointBehaviors.Add(new MyEndpointBehavior());
12 
13             host.Open();
14 
15             Console.WriteLine("服務已經開啟!!!");
16 
17             Console.Read();
18         }
復制代碼

4. 最后我們看一下服務方法

復制代碼
1    public class HomeService : IHomeService
2     {
3         public string Update(string message)
4         {
5             Console.WriteLine("我在Action方法:" + message);
6 
7             return "my reply!!!";
8         }
9     }
復制代碼

?

下面看看效果。。。在效果圖中,你應該看到了。在我的Action中的方法前后各有一段“入站消息”和“出站消息”,是不是很爽???

?

三:操作級別Behavior

  從文章開頭的簡圖中,你應該看到了,Operation級別的Behavior比較多,有“操作啟動器(IOperationInvoker)","參數檢查(IParameterInspector)“,

“消息格式化器(IDispatchMessageFormatter)”等等。。。 為什么說等等這個詞,很簡單啊,,,其實還有很多系統內置的,既然是Operation,那就必

然是針對方法的,還記得OperationContract是怎么套在方法上的嗎??? 是特性,對吧,,,同樣的道理,OperationBehavior也是一樣,那怎么用呢??

同樣也是很簡單的,繼承幾個接口即可。。。

?<1>?IParameterInspector 的玩法

? ?其實沒什么好說的,既然是屬于Operation下面的Behavior,那都是通過特性注入的,而這個IParameterInspector,可以做到類似Mvc的Model驗證,下面

我做個簡單的Action參數長度驗證(長度不超過8個字符)。

1.?IParameterInspector

復制代碼
 1     public class MyIParameterInspector : IParameterInspector2     {3         public int MaxLength { get; set; }4 5         public MyIParameterInspector(int MaxLength)6         {7             this.MaxLength = MaxLength;8         }9 
10         /// <summary>
11         /// 出站的操作
12         /// </summary>
13         /// <param name="operationName"></param>
14         /// <param name="outputs"></param>
15         /// <param name="returnValue"></param>
16         /// <param name="correlationState"></param>
17         public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
18         {
19 
20         }
21 
22         /// <summary>
23         /// 入站的參數
24         /// </summary>
25         /// <param name="operationName"></param>
26         /// <param name="inputs"></param>
27         /// <returns></returns>
28         public object BeforeCall(string operationName, object[] inputs)
29         {
30             foreach (var item in inputs)
31             {
32                 if (Convert.ToString(item).Length > MaxLength)
33                 {
34                     throw new Exception("碼單,長度不能超過 " + MaxLength + " 個長度");
35                 }
36             }
37 
38             return null;
39         }
40     }
復制代碼

2.?IOperationBehavior

復制代碼
 1 public class MyOperationBehavior : Attribute, IOperationBehavior2     {3         public int MaxLength { get; set; }4 5         public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)6         {7 8         }9 
10         public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
11         {
12 
13         }
14 
15         public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
16         {
17             dispatchOperation.ParameterInspectors.Add(new MyIParameterInspector(MaxLength));
18         }
19 
20         public void Validate(OperationDescription operationDescription)
21         {
22 
23         }
24     }
復制代碼

3. 在Action在加上MyOperationBehavior 這個 Attribute

復制代碼
 1     public class HomeService : IHomeService2     {3         [MyOperationBehavior(MaxLength = 5)]4         public string Update(string message)5         {6             Console.WriteLine("我在Action方法:" + message);7 8             return "my reply!!!";9         }
10     }
復制代碼

4. 然后我在客戶端故意輸入大于5的字符,看看效果怎么樣???

復制代碼
 1    public class Program12     {3         static void Main(string[] args)4         {5             HomeServiceClient client = new HomeServiceClient();6 7             client.Update("我故意輸入了很多的字符,哈哈。。。。。");8 9             Console.Read();
10         }
11     }
復制代碼

5. 最后看看效果圖,可以看到,最終的入站消息會拋出一個異常。。。

?

?

<2>?MessageFormatter,IOperationInvoker?的玩法

   剩下的這兩個玩法都差不多,你只需要extends一下,然后加入到OperationBehavior即可,有了上面的思想,我想下面這些使用起來都不是問題吧。。。

?

隨筆- 197? 文章- 0? 評論- 3407?

十五天精通WCF——第十天 學會用SvcConfigEditor來簡化配置

  

? ? ? ?我們在玩wcf項目的時候,都是自己手工編寫system.serviceModel下面的配置,雖然在webconfig中做wcf的服務配置的時候,vs提供大多

數的代碼提示,但對于不太熟悉服務配置的小鳥們來說,有些困難,而且一些服務配置也容易遺漏,大多情況下,我們都是copy一份服務配置,然

后在服務配置上面修修改改,對吧。。。其實呢,.net給我們提供了一個強大的scvconfigeditor這個工具化的軟件來幫助我們生成wcf的配置,是

不是很神奇???

?

一:工具在何處

  當然在無比牛逼的Microsoft SDK下面啦,在C:\Program Files (x86)\Microsoft SDKs\Windows下面,你會找到很多的版本,如下圖:

對吧,你已經看到了很多的版本,當然啦,我肯定要找最新的啦,一禁臠,我進去了v8.0A,如下圖:

C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools

?

你應該也看到了,各種牛逼的工具,很眼饞吧,不過這一篇我們還是看重SvcConfigEditor。

?

二: 如何使用SvcConfigEditor

1. ? 雙擊打開,選擇“文件” => “新建配置”。

?

2. ?然后我們選擇 “新建服務” => “填寫服務名”

?

3. ?然后我們給service定義一個host, 點擊 "主機" => "新建“ => "填寫基址"。

?

4.??到這一步,你是不是特別想看一看生成的config配置是咋樣的???好啊,滿足你的虛榮心,我們只需要點

? ? ?擊"保存“,選擇一個路徑即可。。。

  

5. ?好了,你的虛榮心得到滿足了,下面我們來定義endpoint了,其實也是非常非常簡單的, 點擊”終結點"

? ? => "新建服務終結點",然后我們就象征性的填寫一些Address,Contract,Binding即可,如下圖:

?

6. 上面我們就已經定義了一個basichttpbinding了,下一步的話,我們還記得要公布一個mexhttpbinding,

? ? 這樣我的svcutil才能服務引用,對吧,所以方法也是很簡單,繼續“新建終結點”,如下圖:

  

7. 最后我還記得mex需要有一個behavior,讓http的get可以訪問,有了這個神器,同樣簡單,我們可以

? ? 點擊“高級” => "服務行為" => "新建"。

?

8. 最后我們保存來看一下生成的appconfig是啥樣的???

則么樣???我不需要寫一個字的config配置就完成了基本的服務配置,如果你還想玩高級的,可以自己試著琢磨琢磨SvcConfigEditor。

?

好了,差不多可以睡了,下一篇我們來研究研究 SvcConfigEditor中的診斷工具,很好玩的啦~~~~~

?

隨筆- 197? 文章- 0? 評論- 3407?

十五天精通WCF——第十一天 如何對wcf進行全程監控

  說點題外話,我們在玩asp.net的時候,都知道有一個叼毛玩意叫做“生命周期”,我們可以用httpmodule在先于頁面的page_load中

做一些攔截,這樣做的好處有很多,比如記錄日志,參數過濾,全局登錄驗證等等。。。在wcf里面的話也是有類似的功能,第一種就是在

endpoint中加上runtime的behavior,這樣的話就可以先于“服務方法”做攔截,第二種方法呢,也就是我們這一篇所說的全程監控,俗稱

”診斷功能”。

?

一:診斷

  我也說了,“診斷”這是wcf的一個專業術語,意思也就是監控wcf的所有動向,如果往下說的話,可以分為監控 wcf的message 和 wcf

本身的服務狀態信息和端對端的流轉消息。

1. 端對端的流轉消息

  在玩wcf之前,不知道有多少人熟悉Diagnostics,對的,它就是.net自帶的日志類,當然在這個年代,記錄日志的組件有很多,比如

log4net,Nlog等等。。。不過話說回來,Diagnostics這個叼毛用起來還比較另類,它由“跟蹤源” 和 “監聽器”組成。分別就是TraceSource

來指定跟蹤源,用TraceListener來指定跟蹤源的監聽器,所以理所當然,TraceSource的所有蹤跡都會被TraceListener監聽到,下面我們

看看怎么玩。

復制代碼
<?xml version="1.0" encoding="utf-8"?>
<configuration><system.diagnostics><sources><source name="System.ServiceModel" switchValue="ActivityTracing"><listeners><add name="mylisteners" type="System.Diagnostics.XmlWriterTraceListener" initializeData="E:\1.txt" /></listeners></source></sources><trace autoflush="true"/></system.diagnostics><system.serviceModel><behaviors><serviceBehaviors><behavior><serviceMetadata httpGetEnabled="true" /><serviceDebug includeExceptionDetailInFaults="false" /></behavior></serviceBehaviors></behaviors><services><service name="MyService.HomeService"><endpoint address="HomeService" binding="wsHttpBinding"contract="MyService.IHomeService"><identity><dns value="localhost" /></identity></endpoint><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /><host><baseAddresses><add baseAddress="http://192.168.1.107:1920" /></baseAddresses></host></service></services></system.serviceModel></configuration>
復制代碼

?從上面的配置中可以看到,你有沒有發現我在配置system.diagnostics的時候和wcf一點關系都沒有,我并沒有在system.ServiceModel

下對diagnostics有一丁點的配置,對吧,這說明什么,說明“蹤跡跟蹤”功能和wcf一點關系都沒有,但卻可以完整的記錄wcf的蹤跡信息,然

后我稍微解釋下listeners節點,在這里我配置了一個XmlWriterTraceListener的監聽器,然后把輸出文件的路徑配置在initializeData屬性下,

其實都是diagnostics本身的知識范疇,和wcf一點關系都沒有,好了,下面我開啟下程序,看看到底都追蹤到什么?

有沒有看到,當我的服務啟動之后,追蹤信息就全部來了。。。但是接下來有一個問題來了,這個很雜亂的xml該怎么看才能最舒舒服服的

呢???不用著急啦,wcf同樣給我們提供了一個叫做SvcTraceView的工具,專門就是用來查找這個“蹤跡信息”的,工具的路徑在:

C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools

?

下面的事情就是打開它,附加一下1.txt文件就好了,如下圖:

從左邊的“活動圖”中大概可以看到HomeService這個服務啟動到運行經歷了一些什么樣的悲慘故事。。。有興趣的話,大家可以自己動

手試試啦。

?

2. 監控input和ouput的message

  如果要監控message的話,我們需要再定義一個TraceSource 和 TraceListener即可,不過這次監聽的是System.ServiceModel.

MessageLogging跟蹤源,然后在System.ServiceModel下面配置一下message的參數,如下:

復制代碼
 1 <?xml version="1.0" encoding="utf-8"?>2 <configuration>3 4   <system.diagnostics>5     <sources>6       <source name="System.ServiceModel" switchValue="ActivityTracing">7         <listeners>8           <add name="mylisteners" type="System.Diagnostics.XmlWriterTraceListener" initializeData="E:\1.txt" />9         </listeners>
10       </source>
11       <source name="System.ServiceModel.MessageLogging" switchValue="ActivityTracing">
12         <listeners>
13           <add name="messagelogging" type="System.Diagnostics.XmlWriterTraceListener" initializeData="E:\2.txt"/>
14         </listeners>
15       </source>
16     </sources>
17     <trace autoflush="true"/>
18   </system.diagnostics>
19 
20   <system.serviceModel>
21 
22     <diagnostics>
23       <messageLogging logEntireMessage="true" logMalformedMessages="true"  logMessagesAtTransportLevel="true" />
24     </diagnostics>
25 
26     <behaviors>
27       <serviceBehaviors>
28         <behavior>
29           <serviceMetadata httpGetEnabled="true" />
30           <serviceDebug includeExceptionDetailInFaults="false" />
31         </behavior>
32       </serviceBehaviors>
33     </behaviors>
34 
35     <services>
36       <service name="MyService.HomeService">
37         <endpoint address="HomeService" binding="basicHttpBinding"
38           contract="MyService.IHomeService">
39           <identity>
40             <dns value="localhost" />
41           </identity>
42         </endpoint>
43         <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
44         <host>
45           <baseAddresses>
46             <add baseAddress="http://192.168.1.107:1920" />
47           </baseAddresses>
48         </host>
49       </service>
50     </services>
51 
52   </system.serviceModel>
53 
54 </configuration>
復制代碼

?

這次我準備來跑一下客戶端,調用Server端的Update方法,看看能抓到啥樣的Messsage。

?

?

現在我迫不及待的想用SvcTraceView打開下2.txt,看看都拿到了什么追蹤信息。。。

?

?

好了,這篇我也只是引路式的介紹下SvcTraceView,具體更深入的玩法,大家可以琢磨琢磨,對了,如果大家想對Source和Listener的

一些參數需要進一步了解,可以參考下SvcConfigEditor,比如下面這樣,一目了然,你懂的。。。

?

十五天精通WCF——第十二天 說說wcf中的那幾種序列化

  

  我們都知道wcf是由信道棧組成的,在我們傳輸的參數走到傳輸信道層之前,先需要經過序列化的過程,也就是將參數序列化為message,這篇

我們就來說說這里的序列化,蠻有意思的,可能初學者也明白,在wcf中默認的序列化是DataContractSerializer,確實是這樣,不過wcf在信道中

其實不僅僅支持DataContractSerializer,它還支持其他類型的序列化,比如XmlSerializer,NetDataContractSerializer以及DataContractJson

Serializer,下面我們一起來見證下。

?

1.?XmlSerializer

???要了解XmlSerializer,我們先來簡單看看NetDataContractSerializer,在前面的文章中,我也說過DataContract就是將我們的model序列化為

XSD,第二點就是使用DataContract的原則就是你必須在Model上加DataContract,而且在你要序列化的字段上加DataMember。這樣才能夠正確的序列

化,為了演示,我們先看看默認的序列化Model會變成啥樣?

復制代碼
 1     [DataContract]2     public class Student3     {4         [DataMember]5         public int ID { get; set; }6 7         [DataMember]8         public string Name { get; set; }9 
10         [DataMember]
11         public string SNS { get; set; }
12     }
復制代碼

但是在有些情況下,你可能并不適合用DataContract,比如Model是第三方提供的,那么這個時候你的Model可能就不會有DataContract標記,那這樣的

話wcf就無法進行序列化,那我如果非要保證wcf能正常跑起來的話,還有其他好的辦法嗎???當然了,肯定有辦法,這就好比談戀愛一樣,總不能

在一棵樹上吊死吧,沒人誰離不開誰,也不會誰離開了誰會死,天涯何處無芳草,男兒何患無妻,對吧。Wcf中也一樣,既然DataContract用不了,自

然會有替代它的人,那這個人就是XmlSerializer,使用起來也很簡單,就是在契約方法上面加上XmlSerializerFormat即可,然后我們把Model的

DataContract全部去掉。

?

是不是很簡單,下面我們就要驗證一下,看看這個Format是否進入到了這個Operation的Behavior中,

?

從上面的圖中,你也看到了,?XmlSerializerFormat?已經被注入到Behavior中,并且是由類XmlSerializerOperationBehavior代為處理。

?

接下來,我們用fiddler監視一下,看看Message中的Body是否真的按照XmlSerializer?序列化了。

有沒有看到,這次Message的Body已經和文章開頭處的Message不一樣了。

?

2. NetDataContract

? ? ? ?這個玩意也沒什么好說的,光從表面上看,它和DataContract唯一不同的地方就是多了一個Net,所以你大概也能猜到,這個功能大概和DataCont

ract一樣,只不過比DataContract多了一個程序集保存,那這句話是什么意思呢???就是NetDataContract會把程序集的命名空間和類名都保存到XSD中,

在反序列化的過程中必須要用同樣的程序集才能解開,其實不管我們是做SOA或者面向對象編程都講究接口編程,而NetDataContract給你的印象就是面

向對象編程,當然這也有好處,比如說如果把程序集帶進去就好像秘鑰一樣,必須有它才能解開,對吧,所以導致wcf項目組并不對NetDataContract感冒

,所以在實際應用上也不建議使用。

?

3.?DataContractJsonSerializer

? ?看到上面這個帶有Json的字樣,我想大家都知道這玩意是干什么的???沒錯,他就是將我們的Model序列化成Json,這在wcf的rest編碼使用的很廣,

如果大家有興趣的話,我在下一篇會詳細描述,這里我們先簡單看一看。

?

好了,這一篇就說這些了,洗洗睡了。。。

?

隨筆- 197? 文章- 0? 評論- 3407?

十五天精通WCF——第十三天 用WCF來玩Rest

  

? ? ? ? 在我們玩wcf的時候,都會潛意識的覺得wcf就是通過soap協議交換消息的,并且可以在basic,tcp,msmq等等綁定中任意切換,

牛逼的一塌糊涂,但是呢,如果說哪一天wcf不再使用soap協議,而是采用json格式的字符串,是不是有一點顛覆你對wcf的認識的???

從傳統意義上說,wcf是非常重量級的,很明白的一個例子就是太多太多的配置,尤其是Behavior的配置,而且behavior對wcf來說又是重

中之重,它對wcf的擴展和性能又是最重要的,可恨的是wcf在binding,behavior,contract之中的配置又是非常非常的保守,可以說用

wcf來玩分布式,這些默認配置是完全做不到的,就比如說basicbinding的基類HttpBindingBase。

?

抱怨的話我也不說了,可能微軟也覺得這個問題是個不小的問題,然后就有了輕量級的 asp.net web api,你可以看到它和wcf比起來精

簡多了,也許讓我們這些碼農更加的專注于業務吧,既然wcf帶了這玩意,我也得必須約談一下。

?

一:UriTemplate

  要說rest,還得先說UriTemplate,因為wcf用UriTemplate來做rest中的uri模板匹配,然后用WebInvoke這個OperationBehavior

插入到wcf的心臟中,說的玄乎一點,這個就有點像mvc中的路由匹配機制,下面我舉個例子:

?

1. 用UriTemplate來告知可以監視的完整Url

  從下面的圖中,可以看到三個元素:服務地址,模板,入參(這里面的”1“),這三個元素組合在一起,就構成了完整的remote url,

然后這個完整的url就是我模板(/User/{id})監視的對象。

?

2. 通過UriTemplate來解析url中的參數。

  既然可以構建url,那當然可以解析url啦,對吧,下面這張圖可以很清晰的告知你,當外來的url=http://127.0.1:1920/HomeService

/User/1過來的時候應該被哪個uriTemplate所接收。

?

正是因為UriTemplate具有這樣的url構建和解析能力,所以wcf就把UriTemplate作為WebInvoke和WebGet這兩個屬性的參數來動態

解析外來的url,然后根據這個url分配到具體的服務方法上,下面我們具體看一看。

?

二:WebGet,WebInvoke的使用

  剛才也說了,WebGet和WebInvoke正是用了UriTemplate,才具有了路由轉向的功能,還有就是默認返回的是xml,這里就用json

值作為服務返回的格式

復制代碼
 1     [ServiceContract]2     public interface IHomeService3     {4         [OperationContract]5         [WebGet(UriTemplate = "Get/{id}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]6         Student Get(string id);7 8         [OperationContract]9         [WebInvoke(Method = "POST", UriTemplate = "Add", RequestFormat = WebMessageFormat.Json,
10                    ResponseFormat = WebMessageFormat.Json)]
11         string Add(Student stu);
12     }
復制代碼

對了,Rest推薦使用Http協議中的Get,Post,Delete,Put來作為CURD的狀態機制,然后就是你如果看懂了UriTemplate,那你現在應

該知道這個Template在監視什么類型的url。做完了上面的coding,下面我們需要在webconfig中通過behavior來指定啟動“web編程模型”,

就比如下面這樣。

復制代碼
 1 <?xml version="1.0" encoding="utf-8"?>2 <configuration>3 4   <system.diagnostics>5     <sources>6       <source name="System.ServiceModel" switchValue="ActivityTracing">7         <listeners>8           <add name="mylisteners" type="System.Diagnostics.XmlWriterTraceListener" initializeData="E:\1.txt" />9         </listeners>
10       </source>
11       <source name="System.ServiceModel.MessageLogging" switchValue="ActivityTracing">
12         <listeners>
13           <add name="messagelogging" type="System.Diagnostics.XmlWriterTraceListener" initializeData="E:\2.txt"/>
14         </listeners>
15       </source>
16     </sources>
17     <trace autoflush="true"/>
18   </system.diagnostics>
19 
20   <system.serviceModel>
21 
22     <diagnostics>
23       <messageLogging logEntireMessage="true" logMalformedMessages="true"  logMessagesAtTransportLevel="true" />
24     </diagnostics>
25 
26     <behaviors>
27       <serviceBehaviors>
28         <behavior>
29           <serviceMetadata httpGetEnabled="true" />
30           <serviceDebug includeExceptionDetailInFaults="true" />
31         </behavior>
32       </serviceBehaviors>
33       <endpointBehaviors>
34         <behavior name="webbehavior">
35           <webHttp />
36         </behavior>
37       </endpointBehaviors>
38     </behaviors>
39 
40     <services>
41       <service name="MyService.HomeService">
42         <endpoint address="HomeService" binding="webHttpBinding" behaviorConfiguration="webbehavior"
43           contract="MyService.IHomeService">
44           <identity>
45             <dns value="localhost" />
46           </identity>
47         </endpoint>
48         <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
49         <host>
50           <baseAddresses>
51             <add baseAddress="http://127.0.0.1:1920" />
52           </baseAddresses>
53         </host>
54       </service>
55     </services>
56 
57   </system.serviceModel>
58 
59 </configuration>
復制代碼

?

其實呢?也就是代碼中的WebHttpBehavior類

?

好了,我現在服務地址也出來了:http://127.0.0.1:1920 ,然后服務方法的template也指定了。只要http.sys監控到了template

匹配的url,服務方法就會被執行,比如我現在在瀏覽器里面輸入:http://127.0.0.1:1920/HomeService/Get/1 ?來測試下Get操作。

可以看到,get方法成功了,也正確的匹配了我的服務方法Get。

復制代碼
 1     public class HomeService : IHomeService2     {3         public Student Get(string id)4         {5             return new Student() { ID = Convert.ToInt32(id), Name = "hxc", SNS = "001" };6         }7 8         public string Add(Student stu)9         {
10             return "hello";
11         }
12     }
復制代碼

?

然后我們看看Add方法,我在HttpWebRequest中模擬測試如下。

?View Code

?

?

好了,大概就說這么多了,如果說你不嫌麻煩,你可以用WCF Rest,還有就是不要忘了很多的默認配置,如果你覺得太繁瑣,

可以用用asp.net web api。

?

隨筆- 197? 文章- 0? 評論- 3407?

十五天精通WCF——第十四天 一起聊聊FaultException

  

  ?我們在玩web編程的時候,可能你會不經意的見到一些http500的錯誤,我想你應該不會陌生的,原因你應該也知道,服務器異常嘛,

這時候clr會把這個未處理的異常拋給iis并且包裝成http500的錯誤返回到客戶端,就比如下面這樣。

?

?

從這張圖中,我故意輸入了xss字符,然后的然后,web程序自爆異常,其實我想表達的意思就是,雖然說web程序拋異常了,但不代表iis就

掛了,所以iis還是需要給客戶端做出反饋,這就有了http header,和body信息,同樣的道理,wcf的服務器異常機制也是這樣。。。service

拋出了異常,不代表console就掛了,console要做的事情就是把這個異常包裝起來丟給調用方,而wcf是怎么包裝的呢???就是用了這篇所

說的FaultException。。。

?

一:FaultException

1. faultexception是干什么的?

  剛才我也說了,這個異常就是wcf來包裝遠程錯誤的,具體的類含義就是表示“SOAP錯誤“,如果你夠細心的話,你還會發現到它有個屬性

叫Serializable,有了它,這個叼毛就可以序列化到Soap消息中,對伐???

?

2. 如果挖出faultexception?

  挖出這個exception的方法有很多,比如我來造一個“除以0”的異常,如下所示:

Service:

復制代碼
 1     public class HomeService : IHomeService2     {3         public Student Get(string id)4         {5             //這里必然會拋出異常。。。6             var result = Convert.ToInt32(id) / Convert.ToInt32("0");7 8             return new Student() { ID = Convert.ToInt32(id), Name = "hxc", SNS = "001" };9         }
10     }
復制代碼

Client:

復制代碼
 1     public class Program12     {3         static void Main(string[] args)4         {5             using (HomeServiceClient client = new HomeServiceClient())6             {7                 try8                 {9                     var result = client.Get("1");
10                 }
11                 catch (Exception ex)
12                 {
13 
14                 }
15             }
16         }
17     }
復制代碼

?

看到了沒有,雖然wcf的service已經拋出異常了,但是還是被clr用Faultexception包裝起來了,正如你看到了s:Fault節點,仔細往下看的話,

你還會看到faultcode,faultstring,detail等等屬性節點,那下面有個問題就來了,我們平時在Client端都習慣這么寫。

復制代碼
 1             using (HomeServiceClient client = new HomeServiceClient())2             {3                 try4                 {5                     var result = client.Get("1");6                 }7                 catch (Exception ex)8                 {9                     client.Abort();
10                 }
11             }
復制代碼

但是這么寫有個什么問題呢???就是不管客戶端拋出什么異常,我們都習慣用基類異常Exception捕獲,但是wcf有一點非常惡心的就是,

它的異常信息非常的少,第一眼根本看不出個一二三,這是因為所有的異常你都用頂級的exception捕獲,自然你能知道的信息就非常少,

這也很正常,如果你想要更詳細的信息,你是不是應該在Client端寫上更具體的異常捕獲類呢???就比如你現在已經知道的FaultException

是因為服務器的錯誤都是由它處理的。

?

如果現在你按照上圖中所coding的那樣,你是不是對異常信息可以了解的更深,起碼你知道這個異常的拋出,絕逼是因為通道是正常的,只是

servcie拋出異常了而已。。。那你可能要問了,我這話的言外之意就是還有其他異常類也會捕獲wcf拋出的異常,對的,比如說你的信道出現

故障,這時候會拋出一個“通信異常(CommunicationException)”。

?

三:如何挖出“通信異常”

   ?挖出這個異常,也是很簡單的,現在我們需要使用”會話級別“的binding,比如說nettcpbinding,wshttpbinding,這里的話,我選擇

后者,因為是這樣的,第一次服務器拋異常以后,客戶端和服務器端通信信道就會關閉,如果你在客戶端不重新new一個client,那么這時候你

第二次再使用client的話,這個時候就會產生“信道故障“,拋出CommunicationException,而當你看到CommunicationException的時候,

你可以非常有自信的說,老子的wcf根本就沒有連接到service,而是在client端就被殺死了。。。下面我演示一下。

?

四:自定義FaultException

  現在你應該知道了,只要是Servcie的Exception都會拋出 FaultException,對吧,而且你用Fiddler觀察的話,也看的出其中的faultcode

和faultstring貌似都不是很詳細,那我就有一個想法了,既然wcf會自己給我包裝個FaultException,那何不我自己就在發生異常的時候自己包

裝一個自定義的FaultException,然后我可以包裝一些我自己想要告訴客戶端的信息,這樣的話是不是靈活性非常的大呢???想法很不錯,wcf

也是恩準這么做的,下面我把service的get方法更改如下,在FaultException中自定義Reason,Code,Action等等自定義信息。

復制代碼
 1  public class HomeService : IHomeService2     {3         public Student Get(string id)4         {5             try6             {7                 //這里必然會拋出異常。。。8                 var result = Convert.ToInt32(id) / Convert.ToInt32("0");9 
10                 return new Student() { ID = Convert.ToInt32(id), Name = "hxc", SNS = "001" };
11             }
12             catch (Exception ex)
13             {
14                 var reason = new FaultReason("你這個戰斗力只有五的渣渣。。。 這么簡單的錯誤都出來了,搞個雞巴毛");
15 
16                 var code = new FaultCode("500");
17 
18                 var faultException = new FaultException(reason, code, "是Get這個王八蛋");
19 
20                 throw faultException;
21             }
22         }
23     }
復制代碼

好了,大概就說這么多了,我的目的也很簡單,在寫wcf的client的時候,盡量做到異常越具體越好,這樣方便我們盡可能快的排查問題,因為

wcf的異常信息真的太tmd坑爹了!!!減輕痛苦,從小做起~~~

?

隨筆- 197? 文章- 0? 評論- 3407?

十五天精通WCF——終結篇 那些你需要注意的坑

    

? ? ? ? ? 終于一路走來,到了本系列的最后一篇了,這一篇也沒什么好說的,整體知識框架已經在前面的系列文章中講完了,wcf的配置眾多,如果

不加一些指定配置,你可能會遇到一些災難性的后果,快來一睹為快吧。

?

一: 第一個大坑 【數據傳輸量】

   我們使用wcf的目的,就是用來進行分布式的數據交互,既然是交互,就一定要進行數據交換,可能一些新人并沒有注意到wcf在數據傳輸量上

面做了一個大小限制,比如我現在要傳輸一個2m的txt給service,會出現什么情況???

復制代碼
 1        static void Main(string[] args)2         {3             try4             {5                 var txt = File.ReadAllText("E:\\1.txt");6 7                 HomeServiceClient client = new HomeServiceClient();8 9                 client.Get(txt);
10 
11                 int i = 10;
12 
13             }
14             catch (Exception ex)
15             {
16 
17                 throw;
18             }
19         }
復制代碼

?

可是的可是,我們在玩aspnet的時候,再大的傳輸量都見過,但為什么這玩意就拋異常了呢???下面一個問題就來了,這個傳輸默認值到底

是多少??? 接下來我們就用ILSpy翻翻看。

?

可以看到,這個叼毛玩意居然只有 64k。。。沒錯,你看到的就是64k,也就說明你的傳輸量不能大于64k,否則請求就會在client端拒絕,

知道了原因,我們現在就可以這么修改config了。

    <bindings><netTcpBinding><binding name="MySessionBinding" maxReceivedMessageSize="2147483647"/></netTcpBinding></bindings>

?

有很多資料在配置這個坑的時候,也會使用MaxBufferSize 和?MaxBufferPoolSize,就是用來增加緩沖區和緩沖池的大小。

?

一: 第二個大坑 【并發量太低】

  說起這個大坑,還得先從一段代碼說起,下面是一段對服務進行2w次并發調用,然后我們看看效果。

復制代碼
    public class Program1{static void Main(string[] args){try{for (int i = 0; i < 200000; i++){try{Task.Factory.StartNew((obj) =>{try{HomeServiceClient client = new HomeServiceClient();Console.WriteLine("第 {0} 個請求開始。。。", obj);client.Get("12312");Console.WriteLine("第 {0} 個請求結束。。。", obj);}catch (Exception ex){Console.WriteLine(ex.Message);}}, i);}catch (Exception ex){Console.WriteLine(ex.Message);}}Console.Read();}catch (Exception ex){throw;}}}
復制代碼

?

? ? 從上面你可以看到,當并發數達到800左右的時候,servcie端就開始拒絕client端過來的請求了,并且之后的1min的時間里,client端

開始出現超時異常,這肯定不是我想看到的,?那有人就要說了,我的并發達到800多很正常啊,如果提高這個并發呢???其實在wcf里面

有一個叫做ServiceThrottlingElement綁定元素,它就是用來控制服務端的并發數。

?

這三個屬性的大概意思,我想大家都看的明白,不過有點奇怪的是,這三個屬性的默認值 和 ILSpy中看到的不一樣。。。

?

也懶的研究源碼了,不管怎么樣,反正這三個屬性值都是int類型的,所以我將他們設置為int.maxValue就好了。

復制代碼
<system.serviceModel><behaviors ><serviceBehaviors ><behavior name="nettcpBehavior"><serviceMetadata httpGetEnabled="false" /><!--是否在錯誤中包含有關異常的詳細信息--><serviceDebug includeExceptionDetailInFaults="True" /><serviceThrottling maxConcurrentCalls="2147483647" maxConcurrentInstances="2147483647" maxConcurrentSessions="2147483647" /></behavior></serviceBehaviors></behaviors><bindings><netTcpBinding><binding name="MySessionBinding" /></netTcpBinding></bindings><services><service behaviorConfiguration="nettcpBehavior" name="MyService.HomeService"><endpoint address="net.tcp://127.0.0.1:19200/HomeService" binding="netTcpBinding"bindingConfiguration="MySessionBinding" contract="MyService.IHomeService" /><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /><host><baseAddresses><add baseAddress="http://127.0.0.1:1920" /></baseAddresses></host></service></services></system.serviceModel>
復制代碼

?

然后我們再把程序跑起來看一看。。。

?

? ? ? 現在你可以發現并發早已突破800了,不過你要記住,如果并發數太多,容易造成系統資源耗盡,導致崩潰,這時候負載均衡就來

了,對吧,wcf需要修改的配置還有很多,正因為wcf框架龐大,很多默認配置不符合生產需求,所以大家在工作中需要注意,這個系列

就到此打住了,希望對你有幫助。

轉載于:https://www.cnblogs.com/kingCpp/p/4705931.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/397710.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/397710.shtml
英文地址,請注明出處:http://en.pswp.cn/news/397710.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python如何實現共享報表系統_使用python來實現報表自動化-阿里云開發者社區

xlwt 常用功能xlrd 常用功能xlutils 常用功能xlwt寫Excel時公式的應用xlwt寫入特定目錄(路徑設置)xlwt Python語言中&#xff0c;寫入Excel文件的擴展工具。可以實現指定表單、指定單元格的寫入。支持excel03版到excel2013版。使用時請確保已經安裝python環境。百度百科xlrd Py…

去除inline-block元素間間距的N種方法

這篇文章發布于 2012年04月24日&#xff0c;星期二&#xff0c;22:38&#xff0c;歸類于 css相關。 閱讀 147771 次, 今日 52 次 by zhangxinxu from http://www.zhangxinxu.com 本文地址&#xff1a;http://www.zhangxinxu.com/wordpress/?p2357 一、現象描述 真正意義上的in…

Docker深入淺出2

Docker系統架構 Docker使用客戶端-服務端&#xff08;c/s&#xff09;架構模式&#xff0c;使用遠程api來管理和創建Docker容器。 docker容器通過Docker鏡像來創建。 容器與鏡像的關系類似于面向對象編程中的對象與類的關系 Docker面向對象容器對象鏡像類加速器配置&#xff1a…

mysql安裝包下載密碼_MySQL解壓包的安裝與下載的圖文教程

這篇文章主要為大家詳細介紹了mysql解壓包的安裝基礎教程&#xff0c;具有一定的參考價值&#xff0c;感興趣的小伙伴們可以參考一下由于換了新電腦&#xff0c;所以的環境都要到新電腦去配置。突然發現mysql的配置忘了&#xff0c;然后百度又重新來一遍。特地寫一篇文章記錄一…

php 扒取網頁數據

扒取方法 public function index(){$url http://www.dytt8.net/;// $url Public/txt/movies.txt;$content file_get_contents($url);$content iconv("gb2312", "utf-8//IGNORE",$content);$reg "|<div class\"co_content2\">(.*…

多維DP UVA 11552 Fewest Flop

題目傳送門 1 /*2 題意&#xff1a;將子符串分成k組&#xff0c;每組的字符順序任意&#xff0c;問改變后的字符串最少有多少塊3 三維DP&#xff1a;可以知道&#xff0c;每一組的最少塊是確定的&#xff0c;問題就在于組與組之間可能會合并塊&#xff0c;總塊數會-1。…

多表聯合查詢

關聯數據庫字典表的多表聯合查詢 inner join…on 自動連接 需要用到表的所有信息時&#xff0c;可以用以下兩種方法 1) left join…on… 左連接 &#xff08;以左為準&#xff0c;右邊沒有NULL代替&#xff09; 2) right join…on… 右連接&#xff08;以右為準&#xff…

python elasticsearch update_使用python的elasticsearch部分更新

我有以下格式的elasticsearch文檔。我需要部分更新“x”字段并在其中添加python dict。{"_index": "gdata34","_type": "gdat","_id": "328091-72341-118","_version": 1,"_score": 1,"…

32位與64位注冊表

如果32位系統OFP的注冊表路徑是 HKEY_LOCAL_MACHINE\SOFTWARE\Bohemia Interactive\ 那么在64系統里就應該是 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Bohemia Interactive\ 多了一級Wow6432Node轉載于:https://www.cnblogs.com/zhang-pengcheng/p/4712135.html

http 請求頭和響應頭

客戶端發送請求過程帶著的數據&#xff1a; 1.請求地址 2.請求方式 3.請求頭 request headers 4.請求參數 https://www.juhe.cn/ 130.... 1a2b....pei 服務端響應給客戶端的信息&#xff1a; 1.響應內容 2.響應報文/響應頭部 response headers a 響應頭 b 響應體 3.http狀…

[算法]-排序算法之希爾排序

希爾排序算法思想 希爾排序的實質就是分組插入排序&#xff0c;該方法又稱縮小增量排序.基本思想是&#xff1a;先將整個待排元素序列分割成若干個子序列&#xff08;由相隔某個“增量”的元素組成的&#xff09;分別進行直接插入排序&#xff0c;然后依次縮減增量再進行排序&a…

python tkinter button顏色變不了_tkinter多按鈕顏色變化

我使用tkinter創建一個8x8按鈕矩陣&#xff0c;當按下單個按鈕時&#xff0c;它會添加到最終列表中(例如finalList((0,0)&#xff0c;(5,7)&#xff0c;(6,6)&#xff0c;…)&#xff0c;允許我快速創建8x8(x&#xff0c;y)坐標圖像。我已經創建了一個帶有按鈕的窗口&#xff0…

應用spss可靠性分析軟件

問卷調查的可靠性分析 一、概念&#xff1a; 信度是指依據測驗工具所得到的結果的一致性或穩定性&#xff0c;反映被測特征真實程度的指標。一般而言&#xff0c;兩次或兩個測驗的結果愈是一致。則誤差愈小&#xff0c;所得的信度愈高&#xff0c;它具有下面特性&#xff1a;1、…

springmvc中的單例問題

1&#xff0c;springmvc實際上是基于一個叫做DispatcherServlet的servlet的。servlet按照以往的學習經驗&#xff0c;他是單事例多線程的。 Servlet生命周期 1.裝載Servlet。這項操作一般是動態執行的。然而&#xff0c;Server通常會提供一個管理的選項&#xff0c;用于在Serve…

設計模式 -- 亨元模式(FlyWeight Pattern)

用來盡可能減少內存使用量&#xff0c;適用于存在大量重復對象的場景&#xff0c;達到對象共享&#xff0c;避免創建過多對象的效果&#xff0c;提升性能&#xff0c;避免內存溢出。 定義&#xff1a; 使用共享對象有效支持大量細粒度對象。 適用場景&#xff1a; 系統中存在大…

python3.6使用mysql_Python之——Python3.6連接MySQL

只安裝了Python是不能連接數據庫的&#xff0c;還要安裝Python連接MySQL的相關類庫&#xff0c;Python2.7連接MySQL的類庫很多&#xff0c;MySQL官方最新支持的Python為Python3.4.&#xff0c;如下圖所示&#xff1a;那么&#xff0c;在Python3.6上如何實現連接MySQL的功能呢&a…

android解析json

android2.3提供的json解析類 android的json解析部分都在包org.json下&#xff0c;主要有以下幾個類&#xff1a; JSONObject&#xff1a;可以看作是一個json對象 JSONStringer&#xff1a;json文本構建類 JSONArray&#xff1a;可以看作是json的數組 JSONTokener&#xff1a;js…

MVVM模式于MVP模式

MVC、MVP、MVVM這些模式是為了解決開發過程中的實際問題而提出來的&#xff0c;目前作為主流的幾種架構模式而被廣泛使用。 一.MVP模式(Model-View-Presenter):傳統的開發是MVP模式(例如jquery) MVP是把MVC中的Controller換成了Presenter&#xff08;呈現&#xff09;&#xff…

HUNAN 11560 Yangyang loves AC(二分+貪心)

http://acm.hunnu.edu.cn/online/?actionproblem&typeshow&id11560&courseid0 題意&#xff1a;總共有n天,每天yangyang都需要一個快樂值,有m個隊友,每個隊友都會給陽陽一個快樂值(為2的冪),并且只能給一次,如果某一天隊友給的快樂值達到yangyang需要的快樂值那么…

BrowserSync開發利器

2019獨角獸企業重金招聘Python工程師標準>>> 大大節省開發時間。安裝使用簡單。使用步驟&#xff1a; 1、nodejs環境 安裝 2、在項目中使用npm安裝到本項目 3、對要監聽的文件執行響應命令 官網更詳細&#xff1a;http://www.browsersync.cn/#install 原理&#xf…