Refactor boilerplate code with Actions and Funcs

I have seen quite a few projects where the code starts with the best of intentions in terms of readability, but soon setup and boilerplate code creep in and it becomes hard to focus on where the setup and clean up ends and the functionality begins.  The other downside being repetition of this boilerplate code when doing similar things, such as calling web services, logging, exception handling etc., and being able to manage where this repeated code exists.

With a few examples I hope to provide an idea of what can be done to refactor out any repeated setup and clean up code with the help of the Action and Func delegates.

Let’s say I have a WCF WebService, which I need to setup the environment connection before calling the method, and then have to do some clean up and exception handling afterwards.  This may take the form of:

 public string GetData(int id)
{
    string result;
 
    using (WebServiceClient proxy = new WebServiceClient())
    {
        // do setup...   
        try
        {
            result = proxy.GetData(id);

        }
        catch (ArgumentNullException ex)
        {
            // common exception handling  
        }
        catch (InvalidOperationException ex)
        {
            // common exception handling  
        }
        catch (...)  
        {  
            // common exception handling  
        } 
        finally
        {
            // do cleanup...  
        }
    }

    return result;
}

Which may not look too bad, though when the setup and clean up areas become larger, the key line result = proxy.GetData(id); can get lost in the noise.  Also if you have a standardised way of logging, exception handling etc., if something changes with that standard logging mechanism for example, it is a lot of effort to change everywhere it occurs, and risk some parts being missed. Alternatively it can lead to copy paste errors and introduce new bugs.  This is true of mapping exceptions to fault codes, general tracing within an application and any other standard “non-functional” parts within your code.

If we were to take everything other than the web service call from the code above, then it would be easier to see the intent of this method. If we imagine that GetData isn’t the only method we want to be able to call from this layer, the common code would have to be replicated for each of those calls, making this class a lot noisier.

The Helper Methods

It is possible to encompass all web calls with 2 overrides of a single helper method, i.e. a web service which returns an object, and those which do not.  Utilizing Func (those which return some data) and Action (those which are void), it is possible to create the following helper methods:

 public static void CallWebService(
    Action webServiceToCall,
    WebServiceClient client)
{
    // do setup...   
    try
    {
        webServiceToCall();
    }
    catch (ArgumentNullException ex)
    {
        // common exception handling  
    }
    catch (InvalidOperationException ex)
    {
        // common exception handling  
    }
    catch (...)  
    {  
        // common exception handling  
    }
    finally
    {
        // do cleanup...  
    }
}

and

 

 public static T CallWebService<T>(
    Func<T> webServiceToCall,
    WebServiceClient client)
{
    T result;
 
    // do setup...   
    try
    {
        result = webServiceToCall();
    }
    catch (ArgumentNullException ex)
    {
        // common exception handling  
    }
    catch (InvalidOperationException ex)
    {
        // common exception handling  
    }
    catch (...)  
    {  
         // common exception handling  
    }
    finally
    {
        // do cleanup...  
    }

    return result;
}

 

Where generic type “T” is the return type of our call.

I have passed a reference of the WebServiceClient through so that the setup may modify the proxy object before executing it’s method.  Similarly for clean up after the method is called.

Now with these in place I can modify my GetData method to look like:

 public string GetData(int id)
{
    string result;
 
    using (WebServiceClient proxy = new WebServiceClient())
    {
        result = CallWebService<string>(
            () => proxy.GetData(id),
            proxy);
    }

    return result;
}

 

Here I have specified that my web service is returning a string type, passing the method through the Func<T> as the lambda expression

() => proxy.GetData(id)

and passing the proxy object through so that the CallWebService<T> helper may use it to do setup and clean up operations.

Now if I wish to change my exception handling, I only have to change it in one place instead of everywhere I am handling those types of exceptions.

Using Debug symbols within the helpers

By further utilizing the DEBUG symbols within visual studio, I can easily modify what is logged between debug and the release builds:

 public static T CallWebService<T>(
    Func<T> webServiceToCall,
    WebServiceClient client)
{
    T result;
 
    // do setup...   
DEBUG   
    Trace.WriteLine("...");   
if
    try
    {
        result = webServiceToCall();
    }
    catch (ArgumentNullException ex)
    {
        // common exception handling  
DEBUG  
        Trace.WriteLine("...");  
if
    }
    catch (InvalidOperationException ex)
    {
        // common exception handling  
DEBUG  
        Trace.WriteLine("...");  
if
    }
    catch (...)
    {
        // common exception handling  
DEBUG  
        Trace.WriteLine("...");  
if
    }
    finally
    {
        // do cleanup...  
DEBUG  
        Trace.WriteLine("...");  
if
    }

    return result;
}

Now I can get more verbose logging everywhere within my calls when I am debugging, but will get the original logging when deployed onto a release build. Again I could create a helper method for this type of debug logging if I find that it starts to become repeated, refactoring everything from the #if DEBUG to the #endif.

Generalizing

These ideas are not by any means limited to calling web services, tracing or exception handling.  Almost anything you do within a repeated fashion which cannot be refactored in the standard ways can be considered for this approach.  One place I have found this very useful is within Remote Powershell calls, I have marked the setup and clean up code here with comments to see how much it can take over:

 // Start of setup code   
PSCredential credential = GetCredentials(user, pw);
Runspace runspace = GetRunspace(credential);
 
try
{
    PSCredential readerCred = GetCredentials(readerUser, readerPw);
    PSObject readingConnection = GetConnection(runspace, readerCred);
 
    object connectionSettings;
    PSPropertyInfo connectionProp =
        readingConnection.Properties["ConnectionSettings"];
 
    if (connectionProp != null && connectionProp.IsGettable)
    {
        connectionSettings = connectionProp.Value;
    }
    else
    {
        throw new ConnectionException(...);  
    }
 
    // End of setup code  
 
    ExecutePowershellCommand(runspace, connectionSettings, command);
 
    // Start of cleanup code  
}
catch (...)
{
    // Exception handling  
    throw;
}
catch (...)
{
    // Exception handling  
    throw;
}
finally
{
    if (runspace != null)
    {
        CloseRunspace(runspace);
        runspace = null;
    }
}
// End of cleanup code

As I have around 5 types of methods which are very similar to this, especially the runspace creation and cleanup, by abstracting this out it makes the code more readable, easier to maintain, and simpler to amend for diagnostics.

Happy coding!

Written by Dave Thompson