在用戶端正確的關閉WCF串連!
如果你直接將用戶端調用Close關閉,或者使用using語句,那你將是個悲劇,這點相信使用WCF的同志都知道的,因為ClientBase類的Close()方法被調用後,實際上是關閉了一個網路會話,並且會拋出異常!CommunicationException和TimeoutException!
這似乎違反常理,但確實發生了。因為一般來說Close函數都不會拋出異常。這個問題的解決辦法是使用Try-Catch語句包含Close()方法,然後再異常處理中使用Abort函數釋放資源!
同樣,ClientBase類的IDisposable介面的實現函數Dispose方法中會調用Close函數,使用Reflector查看ClientBase<TChannel> 的代碼便知
所以你使用using語句同樣不能正確關閉WCF串連!而我們使用using語句就是為了不管發生什麼情況,只要離開範圍之後,就會釋放相關資源。而WCF的ClientBase類設計很明顯違反了這一觀念!簡直是坑爹啊!
解決這一問題網上以後很多方案,首先說說微軟的,也許是微軟也意識到這一點,所以微軟在提供的WCF樣本程式中特意包含了一個叫做UsingUsing的樣本,名字聽起來有點怪
主要解決的方法是如下的代碼
1 // This method shows the correct way to clean up a client, including catching the 2 // approprate Exceptions. 3 static void DemonstrateCleanupWithExceptions() 4 { 5 // Create a client 6 CalculatorClient client = new CalculatorClient(); 7 try 8 { 9 // Demonstrate a successful client call.10 Console.WriteLine("Calling client.Add(0.0, 0.0);");11 double addValue = client.Add(0.0, 0.0);12 Console.WriteLine(" client.Add(0.0, 0.0); returned {0}", addValue);13 14 // Demonstrate a failed client call.15 Console.WriteLine("Calling client.Divide(0.0, 0.0);");16 double divideValue = client.Divide(0.0, 0.0);17 Console.WriteLine(" client.Divide(0.0, 0.0); returned {0}", divideValue);18 19 // Do a clean shutdown if everything works. In this sample we do not end up20 // here, but correct code should Close the client if everything was successful.21 Console.WriteLine("Closing the client");22 client.Close();23 }24 catch (CommunicationException e)25 {26 // Because the server suffered an internal server error, it rudely terminated27 // our connection, so we get a CommunicationException.28 Console.WriteLine("Got {0} from Divide.", e.GetType());29 client.Abort();30 }31 catch (TimeoutException e)32 {33 // In this sample we do not end up here, but correct code should catch34 // TimeoutException when calling a client.35 Console.WriteLine("Got {0} from Divide.", e.GetType());36 client.Abort();37 }38 catch (Exception e)39 {40 // In this sample we do not end up here. It is best practice to clean up the41 // client if some unexpected Exception occurs.42 Console.WriteLine("Got unexpected {0} from Divide, rethrowing.", e.GetType());43 client.Abort();44 throw;45 }46 }
其實就是使用try-catch語句捕獲關閉時的異常!試想要是每次都這樣調用WCF服務將會是多麼痛苦的一件事情!網上也有人使用Linq的Expression來解決這一問題
於是我借用函數式編程的思想,設計了一個使用lambda運算式來解決這一問題的方案!
關鍵的函數很簡單,一看便知道是我是怎樣設計的!
1 public static class SvcClient 2 { 3 public static void Invoke<TClient>(Action<TClient> act) 4 where TClient : System.ServiceModel.ICommunicationObject, new() 5 { 6 TClient client = new TClient(); 7 try 8 { 9 act(client);10 client.Close();11 }12 catch (System.ServiceModel.CommunicationException)13 {14 client.Abort();15 }16 catch (TimeoutException)17 {18 client.Abort();19 }20 catch (Exception)21 {22 client.Abort();23 throw;24 }25 }26 }
實際上這一設計的思想就是:將所有調用WCF服務的所有代碼做為一個函數傳入進來,然後我再內部使用try-catch語句包裹整個調用過程,這樣就巧妙的將處理關閉串連異常的代碼與實際調用過程分離開來!
函數的使用過程也比較方便
1 SvcClient.Invoke<Service1Client>(client =>2 {3 //在此處添加調用WCF的代碼4 });
代碼看起來比較簡潔優美,個人比較滿意,而且不用再擔心調用結束後不能正確關閉WCF串連,資源不能正確釋放的問題了!