Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tricky IDisposable Issue

Tags:

c#

dispose

I'm trying to abstract/encapsulate the following code so all client calls don't need to repeat this code. For example, this is a call, from a view model (MVVM) to a 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 original attempt at refactoring was to do this:

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 the call with just one line of code now:

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

However, I get an error the the WcfChannelFactory is already disposed. This makes sense because it really is disposed when the view model tries to use it. But, if I removing the using, then I'm not properly disposing of the WcfChannelFactory. Note, the WcfChannelFactory embeds itself in the WcfClientProxy when CreateChannel() is called. This is why/how the view model is trying to use it after it has been disposed.

How do I abstract this code, to keep my view model calls as simple as possible, while properly disposing WcfChannelFactory? I hope I explained this well enough.

Edit - Solved!

Based on steaks answer, this did it:

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());
like image 491
Bob Horn Avatar asked Mar 24 '23 02:03

Bob Horn


1 Answers

Something like the following may 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 haven't used this pattern much with disposables because I haven't found too much of a need for disposables recently. However, in theory I think I like this pattern, taking a callback that executes inside of a using block, when working with disposables for two reasons:

  1. It's simple
  2. It forces consumers of IDisposables to dispose of the instances correctly...Although I agree (I think) with the C#'s team to not raise compiler warnings when IDisposables aren't disposed of in all execution paths, it's still a bit worrisome.
like image 139
Steven Wexler Avatar answered Apr 01 '23 09:04

Steven Wexler