Reputation: 354
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
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
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:
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.Upvotes: 3