棘手的IDisposable问题

我正在尝试抽象/封装以下代码,因此所有客户端调用都不需要重复此代码。 例如,这是一个从视图模型(MVVM)到WCF服务的调用:

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

我最初的重构尝试是这样做:

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

这允许我的视图模型现在只用一行代码进行调用:

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

但是,我得到一个错误, WcfChannelFactory已经被处理掉了。 这是有道理的,因为它确实在视图模型尝试使用它时处理。 但是,如果我删除using ,那么我没有正确处理WcfChannelFactory 。 注意,当调用CreateChannel()时, WcfChannelFactory将自身嵌入到WcfClientProxy 。 这就是视图模型在处理后尝试使用它的原因/方式。

如何正确处理WcfChannelFactory ,如何抽象此代码,以使我的视图模型调用尽可能简单? 我希望我能够解释得这么好。

编辑 – 解决了!

基于牛排回答,这样做了:

 public static class PrestoWcf { public static T Invoke(Func func) { using (var channelFactory = new WcfChannelFactory(new NetTcpBinding())) { var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"]; IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress)); return func(prestoService); } } } 

这是视图模型调用:

 this.Applications = new ObservableCollection(PrestoWcf.Invoke(service => service.GetAllApplications()).ToList()); 

以下内容可能有所帮助

 public static void UsePrestoService(Action callback) { using (var channelFactory = new WcfChannelFactory(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(service.GetAllApplications().ToList())); 

边注:

我没有将这种模式用于一次性用品,因为我最近没有发现过多需要一次性用品。 但是,从理论上讲,我认为我喜欢这种模式,在使用一次性物品时,在一个使用块内执行回调,原因有两个:

  1. 这很简单
  2. 它迫使IDisposables的消费者正确处理实例……虽然我同意(我认为)C#的团队在IDisposables没有在所有执行路径中被处理时不提出编译器警告,但它仍然有点令人担忧。

你确定在那里使用服务定位器模式吗? 除了它是一个反模式,通过使用InvokeFunc ,我认为将来使用会有一些混乱。 此外,我不认为这种方式会将服务的使用与另一层分开。

我认为通过返回结果,这种方法比使用Func具有更多的SOC。

 public static class PrestoWcf { public static IEnumerable PrestoService { get { IEnumerable appList; using (var channelFactory = new WcfChannelFactory(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; } } } 

清洁,但我仍然不建议使用静态方法。