Hard problem with IDisposable

I am trying to abstract / encapsulate the following code so that all client calls do not need to repeat this code. For example, this is a call from the view model (MVVM) to the WCF service:

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()); } 

My initial refactoring attempt was as follows:

 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 allows my view models to make a call with only one line of code:

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

However, I get an error message that WcfChannelFactory already selected. This makes sense because it is actually used when the view model tries to use it. But, if I delete using , then I do not properly manage WcfChannelFactory . Note: WcfChannelFactory is injected into WcfClientProxy when CreateChannel() called. This is why / how the view model tries to use it after it has been deleted.

How do I draw this code so that it is as simple as possible to display my queries on models, when using WcfChannelFactory ? I hope I explained it quite well.

Change - Solved!

Based on the responses to the steaks, this did the following:

 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); } } } 

And here is the view model call:

 this.Applications = new ObservableCollection<Application>(PrestoWcf.Invoke(service => service.GetAllApplications()).ToList()); 
+4
source share
2 answers

Something like the following might help

 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())); 

Side note:

I didn’t use this template for consumables very often, because lately I have not found too much need for disposable consumables. However, in theory, I think I like this pattern, accepting a callback that runs inside the use block when working with disposable devices for two reasons:

  • It's simple
  • This forces IDisposables consumers to get rid of instances correctly ... Although I agree (I think) with the C # team not to raise compiler warnings when IDisposables are not deleted in all execution paths, this is still a bit of a concern.
+7
source

Are you sure you are using the service locator pattern? Also, this is an anti-pattern using Invoke<T> and Func<T, TResult> , I think there will be some confusion in the future. In addition, I do not think that this method will separate the use of the service to another level.

I think this approach, returning the result, has more SOC than using Func<T, TResult> .

 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; } } } 

A cleaner, but still I do not suggest using a static method.

0
source

Source: https://habr.com/ru/post/1479289/


All Articles