cjw
cjw

Reputation: 354

Implementing IDisposable for WCF service client - Am I doing this right?

I want to safely dispose my WCF client, but I'm not sure if I am implementing IDisposable in the right spot.

I added code to implement IDisposable in the service client inside the Reference.vb file that was created when I added the WCF service as a service reference:

Partial Public Class Service1Client
Inherits System.ServiceModel.ClientBase(Of DataConversionService.IService1)
Implements DataConversionService.IService1, IDisposable

Public Sub New()
  MyBase.New
End Sub

'skipping constructors with multiple parameters...

 Public Sub Dispose() Implements IDisposable.Dispose
  Try
    If State <> CommunicationState.Faulted Then
      Close()
    End If
  Finally
    If State <> CommunicationState.Closed Then
      Abort()
    End If
  End Try
End Sub

Did I add this in the right spot? When debugging, I wasn't able to hit my breakpoint in the Dispose sub.

Any help is appreciated!

Upvotes: 1

Views: 337

Answers (2)

Jorge Del Conde
Jorge Del Conde

Reputation: 361

You don't need to dispose the client. In fact, if you dig deep into the code of ClientBase you'll see how it already implements IDisposable and when it gets disposed it just calls Close() - which has error handling paths to abort if something goes wrong.

Having said this, I would use a pattern similar to the below to close your client channel (you may need to cast to IChannel on Close and Abort):

try
{
    client.Close();
}
catch
{
    client.Abort();
}

Upvotes: 0

yakya
yakya

Reputation: 5210

You were close. Firstly, you shouldn't change that Reference.vb file since it will be overwritten when the service definition gets updated and changing that file is not a good practice.

What you can do instead is using Proxy Pattern. The Proxy will be used to call the methods in services and manage the connection state etc. I will write in C# since I don't know VB but you'll get the idea. I will simplify this as much as I can.

The interface of Proxy class might look like this:

public interface IProxy
{
    /// <summary>
    /// Execute call to service method
    /// </summary>
    /// <typeparam name="TBusiness">Service interface</typeparam>
    /// <typeparam name="TResult">Service method return type</typeparam>
    /// <param name="call">Service method</param>
    /// <returns>Returns what service method returns</returns>
    TResult Execute<TBusiness, TResult>(Func<TBusiness, TResult> call) where TBusiness : class;

    /// <summary>
    /// Execute call to void service method
    /// </summary>
    /// <typeparam name="TBusiness">Service Interface</typeparam>
    /// <param name="call">Service method</param>
    void Execute<TBusiness>(Action<TBusiness> call) where TBusiness : class;
}

As you see, there are two methods in this interface. One of them will be used to call a service method which has a return type, the other one will be used for void methods in the service. You can put non-generic version of these methods to the interface too.

The implementation might be like this:

public class ServiceProxy : IProxy
{
    protected void ExecuteCall<TContract>(Action<TContract> code) where TContract : class
    {
        var contractChannel = default(TContract);
        try
        {
            //Create an instance of service client and call the method
            contractChannel = Activator.CreateInstance<TContract>();
            code(contractChannel);
            ((ICommunicationObject)contractChannel).Close();
        }
        catch (FaultException)
        {
            ((ICommunicationObject)contractChannel).Abort();
        }
        catch (CommunicationException)
        {
            ((ICommunicationObject)contractChannel).Abort();
        }
        catch (TimeoutException)
        {
            ((ICommunicationObject)contractChannel).Abort();
        }
    }

    public TResult Execute<TContract, TResult>(Func<TContract, TResult> call) where TContract : class
    {
        return ExecuteCall(call);
    }

    public void Execute<TContract>(Action<TContract> call) where TContract : class
    {
        ExecuteCall(call);
    }
}

Then, you can use it like this:

var proxy = new ServiceProxy();
proxy.Execute<Service1Client>(a => a.MethodInTheService());

What's great about this approach and how you can make it perfect is:

  • You might not want to create the proxy as new ServiceProxy() but inject IProxy as ServiceProxy and use it as WCF client for now but if it changes to Web API in the future e.g., implement and inject WebApiProxy then.
  • You can use contract interfaces to call the proxy methods.
  • You can do whatever you want in the proxy class like caching the channels, getting the service endpoints from the database etc.

Upvotes: 3

Related Questions