2013-05-06 119 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