Tuesday, July 7, 2009

How to get the Using statement to work when dealing with interfaces directly.

UPDATE 2009-08-04: As pointed out in the comments by Marc L there are some issues involved in using the techniques described below the primary problem concerns the fact that you might incur an exception in the finally block of the using statement this would lead to any code below the using statement to not run. Also if you throw an exception in your code and the dispose call yeilds an exception this will mask your exception which you will never see. You can read more about it in this msdn article: Avoiding Problems with the Using Statement

Just recently I did some coding in a project and was faced with something that took me awhile to figure out so I thought I'd write a line or two about it. But before we continue let me state that this post deals with WCF based services in a VB.NET application.

The thing is that personally I'm not very fond of using the "Add Service Reference" in Visual Studio (there are obviously situations where it is useful but most of the times I prefer to have more control over the generated proxy), so what I usually end up doing is to spawn up my own proxy using the ChannelFactory and simply add a reference to the assembly containing the service contracts.

The problem arises when you start working with interfaces to make sure that you can inject your dependencies into your code to be able to implementing proper unit-test. When you do this and try to use the Using statement to implement the disposable pattern and make sure your resources are release in a timely fashion.

Dim factory As ChannelFactory(Of IMyService) = New ChannelFactory(Of IMyService)("MyBinding")
Dim myInterface as IMyService = factory.CreateChannel()

Using myInterface
End Using
This example code will generate the following complier error:

'Using' operand of type '…IMyService' must implement 'System.IDisposable'.

So after banging my head against the monitor for awhile I stumbled upon a blog with an example where the author used the As operator to perform a cast in the using statement, so I figured that this hade to be doable in VB.NET as well.

Dim factory As ChannelFactory(Of IMyService) = New ChannelFactory(Of IMyService)("MyBinding")
Dim myInterface as IMyService = factory.CreateChannel()

Using DirectCast(myInterface, IDisposable)
End Using

The above code will work and even though it isn’t as slick as it’s counterpart in C# it will do the trick. Now we are able to both utilize the disposable pattern to make sure we are not forgetting to release our resources and still make use of dependency injection in our code and improve our testability.

There is no magic here once you think a little about it what the using statement wants is simply a reference to an instance that implements the IDisposable interface which the proxy returned by CreateChannel does. So we just need to give the compiler a little nudge to get it to work.

UPDATE 2009-08-04: However the problems don't stop here. If we wrap an object implementing IDisposable inside a using statement and the Dispose method throws exceptions we are toast. This will lead to all kind of weird behaviours when dealing with error situations.

First out we need to handle the fact that any exception being raised within the using block will be supressed by exceptions raised from inside the Dispose call. Secondly we have the issue that if you have any code that needs to be executed after the using block, you will be in for a nasty suprise since it will not run if an exception is raised from within the dispose method.

The easiest way to get around this would be to implement a helper object dealing with the disposing logic that we can pass to the Using statement. This object can then supress any exceptions being raised during disposal and do the appropriate logging. The code below implements such a wrapper object that will handle this for the WCF scenario, you could easily modify it and just pass in any object implementing IDisposable and just deal with the logging and exception suppression.

Imports System
Imports System.Diagnostics
Imports System.ServiceModel

Namespace DisposablePattern

Public Class CommunicationObjectScope
Implements IDisposable

Private scoped As ICommunicationObject
Private disposed As Boolean = False

Public Sub New(ByVal co As Object)
If TypeOf co Is ICommunicationObject Then
scoped = co
scoped = Nothing
End If
End Sub

Public Sub Dispose() Implements IDisposable.Dispose
If Not disposed Then
If Not scoped Is Nothing Then
Select Case scoped.State
Case CommunicationState.Faulted
Exit Select
Case Else
Exit Select
End Select
End If
End If
Catch ex As Exception
'TODO: replace with more approriate logging!!!

'NOTE: Do NOT throw or rethrow from within this
' exception block since this will mask the
' original exception.
End Try
End Sub
End Class
End Namespace

By using this class we would end up with code looking like this:

Dim factory As ChannelFactory(Of IMyService) = New ChannelFactory(Of IMyService)("MyBinding")
Dim myInterface as IMyService = factory.CreateChannel()

Using co As New CommunicationObjectScope(myInterface)
End Using

To summarize you can use the DirectCast approach if you have controll of the object implementing IDispsable, but if there is the slightest chance that an exception might occur from within the Dispose method you should go for the wrapper approach.