Putting the Func in .net: Passing Func as an Argument

25 Sep 2013 posted by @NickBlair

Firstly, apologies for the title. Moving along swiftly we have long been fans of the MVC4 WebApi and the simplicity it brings to making a restful service. It’s prompted us to detangle a few legacy systems into decoupled restful services that are easier to work with and think about.

Our journey into restful system architectures has led us to a solution that uses http polling. This means that a request to do some long running task is placed on a queue for a background task to pick up and complete. In the meantime, a client who is waiting for the result of the task polls the restful service with http requests until the task is complete and the result is ready.

We have a .net implementation of the Consuming Client that impatiently asks if the result is ready yet. You might assume the calling code would look something like the following:

// Client Code example calling a Restful Service

public class OrderService
{
	HttpRequestRetrier _retrier = new HttpRequestRetrier();

	public Order GetOrder(int id)
	{
		var uri = "http://orders.com/?id=" + id;
		var request = new HttpRequestMessage(HttpMethod.Get, uri);
		var response = _retrier.GetSuccessfulResponse(request);
		var order = response.Content.ReadAsAsync<Order>().Result;
		return order;
	}
}

public class HttpRequestRetrier
{
	private static readonly HttpClient Client = new HttpClient();

	public HttpResponseMessage GetSuccessfulResponse(HttpRequestMessage request)
	{
		var response = SendRequest(request);
		return response.IsSuccessStatusCode ?
			   response :
			   GetSuccessfulResponse(request);
	}
	
	private HttpResponseMessage SendRequest(HttpRequestMessage request)
	{
		var response = Client.SendAsync(request).Result;
		return response;
	}
}

A customised HttpRequestMessage object is passed from the OrderService to the HttpRequestRetrier. With the maximmum retry logic removed for brevity, we can see that this method simply retries sending the request using recursion until it receives a response with a successful status code.

That is until runtime, of course.

If the first response received is not successful, the second attempt results in an InvalidOperationException with the message: ‘The request message was already sent. Cannot send the same request message multiple times’. This is because HttpRequestMessage implements IDisposable which is called after the Request is sent.

This is troubling at first because it seems that either we have to pull the Response checking logic up into the Service where it doesn’t belong. Alternatively, we could push the ability to build the Request down into the Retrier. However, this is also undesirable because we want this to be a utility class that can be used in multiple places. Having the Retrier know how to build the Request for an order breaks the Single Responsibility Principle and clouds the true purpose of the class.

The third option is to use a Func. Instead of passing the actual instance of the Request from the Service to the Retrier we pass the ability to get the instance. The Service can then execute the this function as many times as it needs to. Each time it will be returned a brand new Request instance constructed with the logic owned by the Service. This keeps our Retrier clean and reusable. The change to accommodate this ability is shown below.

// Client Code example calling a Restful Service using Func

public class OrderService
{
	HttpRequestRetrier _retrier = new HttpRequestRetrier();

	public Order GetOrder(int id)
	{
		var response = _retrier.GetSuccessfulResponse(() => GetRequestForOrder(id));
        var order = response.Content.ReadAsAsync<Order>().Result;
		return order;
	}
	
	private HttpRequestMessage GetRequestForOrder(int id)
	{
		var uri = "http://orders.com/?id=" + id;
		var request = new HttpRequestMessage(HttpMethod.Get, uri);
		return request;
	}
}

public class HttpRequestRetrier
{
	private static readonly HttpClient Client = new HttpClient();

    public HttpResponseMessage GetSuccessfulResponse(Func<HttpRequestMessage> requestFunc)
	{
		var request = requestFunc();
		var response = SendRequest(request);
		return response.IsSuccessStatusCode ?
			   response :
			   GetSuccessfulResponse(requestFunc);
	}

    private HttpResponseMessage SendRequest(HttpRequestMessage request)
	{
		var response = Client.SendAsync(request).Result;
		return response;
	}
}

You will notice the argument of the Retrier’s GetSuccessfulResponse method has changed from a HttpRequestMessage to a Func<HttpRequestMessage>. This is the trick to passing the ability. We now need to use a lambda statement in the calling code to declare a function (Func) that returns a Request. We have accommodated this in the OrderService by creating a new method to do exactly that: GetRequestForOrder. Notice that even though this method is private to the Service it can still be called from the Retrier class.

Inside the Retrier the function is then executed to return a new Request instance. This instance is then sent only once to get a Response. If the reponse is successful we can return it, otherwise we can call the GetSuccessfulResponse mehtod again recursively and pass the same function that was received as an argument to be executed again. This routine can be performed continuously until the status code is successful.

Make sure you do not put this code into production without adding maximum retry logic or you’ll be up to your eyeballs in stackoverflow exceptions.

The end result is that we get to maintain the intention in the code’s organisation and still get around the problem of only being able to send a request once.