HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

.NET WCF Activator for sync and async calls

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
callsasyncactivatornetforandwcfsync

Problem

I decided to re-write my code responsible for WCF calls, as all of my methods had try-catch-finally blocks. I read that it is bad idea to use a using() statement as it does not close the WCF connection. After digging around, I found a solution based on delegates. Yesterday I decided to add an activator for calls that are made by async controllers in my MVC Web API app.

I am not sure about the code used for async. My doubts are based on this article.

Note - some code removed for brevity.

Here is the code for activators:

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Threading.Tasks;
using System.Web;

namespace MyProject.Concrete
{
public class SvcActivator
{
public delegate void ServiceMethod(T proxy);

private BasicHttpBinding binding
{
get
{
return new BasicHttpBinding
{
MaxReceivedMessageSize = int.MaxValue,
MaxBufferPoolSize = int.MaxValue
};
}
}

public static void Use(ServiceMethod method, string url)
{
var channelFactory = new ChannelFactory(binding);
var endpoit = new EndpointAddress(url);
IClientChannel proxy = (IClientChannel)channelFactory.CreateChannel(endpoit);
bool success = false;

try
{
method((T)proxy);
proxy.Close();
success = true;
}
catch (Exception)
{
throw;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}

public static Task UseAsync(ServiceMethod method, string url, HttpBindingBase binding)
{
var channelFactory = new ChannelFactory(binding);
var endpoit = new End

Solution

You don't want to use Task.Run from WebAPI (or other service frameworks) because that interferes with the ASP.NET thread pool heuristics.

Instead, use the naturally-asynchronous methods that are available in your WCF client proxy. (Side note: if the proxy is from a very old Visual Studio version, you may need to recreate it).

Then you can look at making your SvcActivator truly asynchronous. First, you'll need an asynchronous delegate type:

public delegate Task ServiceMethodAsync(T proxy);


Next, your UseAsync method:

public static async Task UseAsync(ServiceMethodAsync method, string url)
{
    var channelFactory = new ChannelFactory(binding);
    var endpoit = new EndpointAddress(url);
    IClientChannel proxy = (IClientChannel)channelFactory.CreateChannel(endpoit);
    bool success = false;

    try
    {
        await method((T)proxy);
        proxy.Close();
        success = true;
    }
    catch (Exception)
    {
        throw;
    }
    finally
    {
        if (!success)
        {
            proxy.Abort();
        }
    }
}


Your DAL can use it as such:

public async Task GetOrderAsync(int id)
{
    Order order = null;
    await SvcActivator.UseAsync(async svc =>
    {
        order = await svc.GetOrderAsync(id);
    }, "url_goes_here");
    return order;
}


The end result is that you are using async all the way, rather than wrapping a synchronous WCF call (GetOrder) within a Task.Run.

Code Snippets

public delegate Task ServiceMethodAsync(T proxy);
public static async Task UseAsync(ServiceMethodAsync method, string url)
{
    var channelFactory = new ChannelFactory<T>(binding);
    var endpoit = new EndpointAddress(url);
    IClientChannel proxy = (IClientChannel)channelFactory.CreateChannel(endpoit);
    bool success = false;

    try
    {
        await method((T)proxy);
        proxy.Close();
        success = true;
    }
    catch (Exception)
    {
        throw;
    }
    finally
    {
        if (!success)
        {
            proxy.Abort();
        }
    }
}
public async Task<Order> GetOrderAsync(int id)
{
    Order order = null;
    await SvcActivator<IOrdersService>.UseAsync(async svc =>
    {
        order = await svc.GetOrderAsync(id);
    }, "url_goes_here");
    return order;
}

Context

StackExchange Code Review Q#57656, answer score: 2

Revisions (0)

No revisions yet.