views:

1508

answers:

4

In one of the WCF tutorials, I saw the followign sample code:

Dim service as ...(a WCF service )

try

   ..

   service.close()

catch ex as Exception()
  ... 

   service.abort()

end try

Is this the correct way to ensure that resources (i.e. connections) are released even under error conditions?


Thanks for the answers guys! I upvoted you all.

+2  A: 

I've had good luck with this model:

Dim service As New MyService()
Dim closed As Boolean = False
Try
    service.Open()
    If Not service.State = ServiceModel.CommunicationState.Opened Then
        ''Handle a not-opened state here
    End If
    service.MyMethod()
    service.Close()
    closed = true
Catch ex As Exception
    ''Handle errors here
Finally
    If Not closed Then
        service.Abort()
    End If
End Try
service = Nothing
Chris Porter
+1  A: 

You've got the general idea correct. I've used the following extension method to keep the lines of repetitive code to a minimum.

public static class ICommunicationObjectExtensions
{       
   public static void SafelyCloseConnection(this ICommunicationObject objectToClose)
   {
      bool success = false;

      try
      {
         objectToClose.Close();
         success = true;
      }
      finally
      {
         if (!success)
         {
            objectToClose.Abort();
         }
      }
   }
}

Example of code using this extension method:

HelloWorldServiceClient client = new HelloWorldServiceClient();
HelloWorldDataContract dc = new HelloWorldDataContract();

try
{
   client.Open();
   dc = client.SayHello();
}  // Add catch blocks here for anything you want to handle.
finally
{
   client.SafelyCloseConnection();
}

Of course this is C#, but I think that should still be of help.

Daniel Auger
+8  A: 

See Indisposable: WCF Gotcha #1, where he comes up with a convenient wrapper method:

public delegate void UseServiceDelegate<T>(T proxy);

public static class Service<T>
{
    public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");

    public static void Use(UseServiceDelegate<T> codeBlock)
    {
        var proxy = (IClientChannel)_channelFactory.CreateChannel();
        var success = false;
        try
        {
            codeBlock((T)proxy);
            proxy.Close();
            success = true;
        }
        finally
        {
            if (!success)
            {
                proxy.Abort();
            }
        }
    }
}

Usage:

Service<IOrderService>.Use(
    orderService =>
        {
            orderService.PlaceOrder(request);
        });
John Saunders
This is really very handy. The "WCF Gotcha #1" above link no longer works, and can now be found here: http://old.iserviceoriented.com/blog/post/Indisposable+-+WCF+Gotcha+1.aspx
Xiaofu
@Xiaofu: fixed the link. Thanks.
John Saunders
A: 

If you use a client side cache, you might consider using Expression Trees (see http://thegrenade.blogspot.com/2009/07/using-expression-trees-for-more-elegant.html):

private static TEntity GetItem<TProxy, TEntity, TIdentity>(Expression<Func<TProxy, TIdentity, TEntity>> expression, TProxy proxy, TIdentity id)
    where TEntity : class
    where TProxy : ICommunicationObject
{
    TEntity item = Cache.GetItem<TEntity, TIdentity>(id);
    if (item == null)
    {
        try
        {
            var originalDelegate = expression.Compile();
            item = originalDelegate.Invoke(proxy, id);
        }
        finally
        {
            try{ proxy.Close(); }
            finally { proxy.Abort(); }
        }
        Cache.AddItem<TEntity, TIdentity>(item);
    }
    return item;
}

Usage:

Product p = GetItem((client, identifier) => client.GetProduct(identifier), new CatalogServiceClient(), 123);
grenade
Rob, I don't see how your answer applies to this question.
John Saunders
Perhaps the method example I've given is a little too specific for a generic service call wrapper. In actual fact, you need method overloads for service methods that take more than one parameter and this example does not go into that detail. However many WCF solutions contain groups of very similar methods that simply return different types in response to a get with an id parameter. And you are correct that my finally block is also missing a nested try / finally for the abort.
grenade
@grenade: I think you may need to read the question over again. Your answer doesn't address the question in any way.
John Saunders