What is the best workaround for the WCF client `using` block issue?
Actually, although I blogged (see Luke's answer), I think this is better than my IDisposable wrapper. Typical code:
Service<IOrderService>.Use(orderService=>
{
orderService.PlaceOrder(request);
});
(edit per comments)
Since Use
returns void, the easiest way to handle return values is via a captured variable:
int newOrderId = 0; // need a value for definite assignment
Service<IOrderService>.Use(orderService=>
{
newOrderId = orderService.PlaceOrder(request);
});
Console.WriteLine(newOrderId); // should be updated
Problems with the Using Statement and WCF client
The core of the problem is: at the end of your using
block (which generally is a very good idea to have!), the WCF proxy will be disposed. However, during disposing of the WCF proxy, exceptions can occur - and those will cause the app to misbehave. Since this is done implicitly at the end of the using
block, you might not even really see where the error occurs.
So typically, Microsoft recommends a pattern something like this:
private ServiceClient proxy;
try
{
proxy = new ServiceClient("ConfigName", "http://serviceaddress//service.svc");
string result = proxy.Method();
proxy.Close();
}
catch (CommunicationException e)
{
// possibly log error, possibly clean up
proxy.Abort();
}
catch (TimeoutException e)
{
// possibly log error, possibly clean up
proxy.Abort();
}
catch (Exception e)
{
// possibly log error, possibly clean up
proxy.Abort();
throw;
}
You need to call the proxy.Close()
method explicitly and be prepared to handle any exceptions that might occur from that call, too.
Disposing of a WCF client instantiated via an interface
I recently wrote an article about the correct handling of a WCF client's life cycle: Only wrapping the instantiation in a using
block is not sufficient...
Have a look at http://blog.rsuter.com/?p=975
Summary: Overload Dispose
the following way to use the client with the using
keyword:
public void Dispose()
{
if (isDisposed)
return;
try
{
if (service.State == CommunicationState.Faulted)
service.Abort();
else
{
try
{
service.Close();
}
catch (Exception closeException)
{
try
{
service.Abort();
}
catch (Exception abortException)
{
throw new AggregateException(closeException, abortException);
}
throw;
}
}
}
finally
{
isDisposed = true;
}
}
Does the C# WCF Proxy ClientBaseT Disposal issue still exist in .Net 4.5+
UPDATE: I've just found this... https://msdn.microsoft.com/en-us/library/aa355056(v=vs.110).aspx ...which suggests it's still an issue.
WCF Client - Best Practise
Visual Studio Generator
You can ask Visual Studio to build a client for you, right-clicking your client project and adding a Service Reference
. There's a dialog where you can either type your service url or discover it from within the solution.
Creating a Client
You can build the client class inheriting from ClientBase<ISecurityManager>, ISecurityManager
. Being an operation example on this client class:
public void ExampleMethod(int id)
{
Channel.ExampleMethod(id);
}
Like a real man does
Or without any client class, just calling it:
ServiceInvokerinvoker invoker = new ServiceInvoker();
var result = invoker.InvokeService<ISecurityManager, ReturnType>( proxy => proxy.ExampleMethod(1) );
Last two options assuming you already have configured the ISecurityManager
client:
<client>
<endpoint name="ServiceName"
address="http://ServiceName.test/Service"
binding="basicHttpBinding"
contract="ISecurityManager" />
</client>
Best approach for WCF client
The first question is whether your server needs to maintain any state about the client directly (i.e. are you doing session-like transactions?) If you are, you will need to be able to manage how the server holds the information between communications.
My initial feeling of your question is that if there is no need to leave a connection open, then close it each time and recreate a new connection on demand. This will avoid issues where a connection can be placed into a faulted state between calls. The overhead of creating and destroying connections is minimal, and it will (probably) save you a lot of time in debugging when something goes wrong.
Disposing a WCF windows service client inside a WCF web service
Yes do not use dispose. Do it like this:
var client = new ...;
try {
// Do work
// Everything went well so close the client
client.Close();
}
catch( Exception ex ) {
// Something went wrong so call abort
client.Abort();
// Other logging code
}
if( client.State != System.ServiceModel.CommunicationState.Closed ) {
client.Close();
}
Calling Close()
on client notifies the service instance that it is no longer in use and may be collected by GC (subject to service instance management).
You may wonder why Abort
in the catch
block? The reason is:
Given the WCF binding use transport sessions, the client after a fault would not even be able to close it (if there was no transport layer session then the client could use or close the proxy, but this is not recommended as the configuration of sessions could change). So after a fault has happened the only safe operation is to abort a proxy.
See this for more on Abort
vs Close
.
EDIT
In your comments you asked:
Do you recommend creating and closing a service client like this within every function the web service calls the windows service?
No I don't think that is necessary. Let's see you are calling the WCF service from a web application (ASP MVC), then you would do something like this in the Dispose
method of the controller since ClientBase<TChannel>
implements ICommunicationObject
:
protected override void Dispose(bool disposing) {
base.Dispose( disposing );
ServiceHelper.DisposeService( ( this._whateverServiceClient as ICommunicationObject ) );
}
And here is the ServiceHelper
class that you can use from anywhere:
public static class ServiceHelper {
/// <summary>
/// Disposes the given service.
/// </summary>
/// <param name="service">The service.</param>
public static void DisposeService(ICommunicationObject service) {
if( service != null ) {
bool abort = true;
try {
if( service.State == CommunicationState.Opened || service.State == CommunicationState.Opening ) {
service.Close();
abort = false;
}
}
finally {
// Determine if we need to Abort the communication object.
if( abort )
service.Abort();
}
}
}
}
The idea would be the same if you were calling it from another client.
Related Topics
Passing Strings from C# to C++ Dll and Back - Minimal Example
Does Page Reload Ever Cause Post
How to Create an Excel (.Xls and .Xlsx) File in C# Without Installing Microsoft Office
How to Call Asynchronous Method from Synchronous Method in C#
The Calling Thread Cannot Access This Object Because a Different Thread Owns It
How to Obtain the Frequencies of Each Value in an Fft
One Dbcontext Per Web Request... Why
Output Log Using Ftpwebrequest
A Field Initializer Cannot Reference the Nonstatic Field, Method, or Property
Mvc5 Razor Html.Dropdownlistfor Set Selected When Value Is in Array
Create an Instance of a Class from a String
Parallel Foreach With Asynchronous Lambda
Arraylist VS List≪≫ in C#
Entityframework - Contains Query of Composite Key