If you're trying to be a conscientious developer and making sure that you cleanup your resources - great! You are writing 'using()
' blocks around all of your disposable items - great... except when the disposable item is a WCF Client/Proxy! The using()
statement and the try
/finally
effectively have the same IL:
using (var win = new Form())
{
}
Form f = null
try
{
f = new Form()
}
finally
{
if (f != null)
{
f.Dispose()
}
}
Here's the IL for the 'using()
' block above compiled in Release mode:
IL_0000: newobj instance void [System.Windows.Forms]System.Windows.Forms.Form::.ctor()
IL_0005: stloc.0
.try
{
IL_0006: leave.s IL_0012
}
finally
{
IL_0008: ldloc.0
IL_0009: brfalse.s IL_0011
IL_000b: ldloc.0
IL_000c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0011: endfinally
}
Here's the IL for the second block (try
/finally
) compiled in Release mode:
IL_0012: ldnull
IL_0013: stloc.1
.try
{
IL_0014: newobj instance void [System.Windows.Forms]System.Windows.Forms.Form::.ctor()
IL_0019: stloc.1
IL_001a: leave.s IL_0026
}
finally
{
IL_001c: ldloc.1
IL_001d: brfalse.s IL_0025
IL_001f: ldloc.1
IL_0020: callvirt instance void [System]System.ComponentModel.Component::Dispose()
IL_0025: endfinally
}
As you can see, the IL is nearly identical.
Well, this is all fine and good but let's get back to the issue with WCF. The problem is that if an exception is thrown during disposal of the WCF client/proxy, the channel is never closed. Now, in general, any exception that occurs during disposal of an object is indeed undesirable. But, in the case of WCF, multiple channels remaining open could easily cause your entire service to fall on its face - not to mention what might eventually happen to your web server.
Here is an alternative solution that can be used:
WCFProxy variableName = null;
try
{
variableName = new WCFProxy();
variableName.Close();
}
catch (Exception)
{
if (variableName != null)
{
variableName.Abort();
}
throw;
}
MSDN does have a brief on this issue which you can read here.