2013-05-06 64 views
4

我正在嘗試抽象/封裝以下代碼,以便所有客戶端調用都不需要重複此代碼。例如,這是一個電話,從一個視圖模型(MVVM)到WCF服務:Tricky IDisposable問題

using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding())) 
{ 
    var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"]; 
    IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress));  
    this.Applications = new ObservableCollection<Application>(prestoService.GetAllApplications().ToList()); 
} 

我在重構原始嘗試是要做到這一點:

public static class PrestoWcf 
{ 
    public static IPrestoService PrestoService 
    { 
     get 
     { 
      using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding())) 
      { 
       var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"];  
       return channelFactory.CreateChannel(new EndpointAddress(endpointAddress)); 
      } 
     } 
    } 
} 

這使得我的觀點模型撥打電話只有一個,現在的代碼行:

this.Applications = new ObservableCollection<Application>(PrestoWcf.PrestoService.GetAllApplications().ToList()); 

但是,我得到了WcfChannelFactory已經配置錯誤。這是有道理的,因爲當視圖模型嘗試使用它時,它確實被放棄了。但是,如果我刪除了using,那麼我沒有正確處理WcfChannelFactory。請注意,當CreateChannel()被調用時,WcfChannelFactory嵌入WcfClientProxy中。這就是爲什麼視圖模型在處理完成後試圖使用它的原因。

如何抽象此代碼,以保持我的視圖模型調用盡可能簡單,同時正確處理WcfChannelFactory?我希望我解釋得很好。

編輯 - 已解決!

基於牛排答案,這做到了:

public static class PrestoWcf 
{ 
    public static T Invoke<T>(Func<IPrestoService, T> func) 
    { 
     using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding())) 
     { 
      var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"]; 

      IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress)); 
      return func(prestoService); 
     } 
    } 
} 

這裏是視圖模型電話:

this.Applications = new ObservableCollection<Application>(PrestoWcf.Invoke(service => service.GetAllApplications()).ToList()); 
+0

+1使用Func返回您的應用程序,而不是帶有副作用的操作! – 2013-05-06 02:54:03

回答

7

像下面這樣可以幫助

public static void UsePrestoService(Action<IPrestoService> callback) 
{ 
    using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding())) 
    { 
     var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"]; 
     IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress)); 
     //Now you have access to the service before the channel factory is disposed. But you don't have to worry about disposing the channel factory. 
     callback(prestoService); 
    } 
} 

UsePrestoService(service => this.Applications = new ObservableCollection<Application>(service.GetAllApplications().ToList())); 

方備註:

我一直沒有用過這種模式,因爲我最近沒有發現太多的一次性用品需求。然而,在理論上我覺得我喜歡這種模式,採取的是一個使用塊,用一次性的工作有兩個原因,當內部執行的回調:

  1. 很簡單
  2. 它迫使IDisposables處置的消費者 實例正確...雖然我同意(我認爲)與C#的團隊 不會在IDisposables在所有執行路徑中丟棄 時引發編譯器警告,但它仍然有點令人擔憂。
+2

+1,我喜歡這個解決方案。 – Gjeltema 2013-05-06 02:28:45

+0

+1好的解決方案! – ppetrov 2013-05-06 02:59:01

0

您確定要在那裏使用服務定位符模式嗎?在它旁邊是一個反模式,通過使用Invoke<T>Func<T, TResult>,我想在未來的使用中會有一些混淆。此外,我不認爲這樣會將服務的使用分離到另一層。

我認爲這種方法通過返回結果,比使用Func<T, TResult>具有更多的SOC。

public static class PrestoWcf 
{ 
    public static IEnumerable<Application> PrestoService 
    { 
     get 
     { 
      IEnumerable<Application> appList; 
      using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding())) 
      { 
       var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"];  
       appList = prestoService.GetAllApplications().ToArray(); //you can ignore the .ToArray, I just not sure whether the enumerable still keep the reference to service 
      } 
      return appList; 
     } 
    } 
} 

清潔,但我仍然不建議使用靜態方法。

+0

對不起,但我不確定你的意思。 Presto服務並不總是返回列表。我的例子只是一個電話。如果我採用你的方法,那麼我必須爲服務上的每個現有方法創建一個新方法。 – 2013-05-06 13:02:34

+0

那麼,沒有說服務並不總是返回'名單'。此外,我只強調,使用'static T Invoke (Func func)'有點太抽象了(對我來說,它就像一個服務定位器)。如果用於業務層,它可能是維護地獄。 – Fendy 2013-05-06 13:17:51

+0

謝謝,但這都是客戶端。它只是一個幫手,所以我們不必在每次調用服務時都重複這個相同的代碼。 – 2013-05-06 13:59:50