Reusable Fixture in C#

Published 12 March 9 2:23 PM | Phil Gilmore

CSharp_GreenCloud_oilPhil Gilmore

I was recently working on an application that had had 2 problems.  I found that at one level the code repeats itself many times over.  Every method in the business layer used the same try / catch / catch structure to wrap calls to the data layer.  They were all 95% identical to each other.  In another level, some WCF connections were not being closed properly in all instances.  Both issues were fixture issues.  I found that I could fix them both with a minimum of code repetition  when using fixture.  Let me show some examples of what was being done with the exception handling structures.  There are methods that return a type and some return void.  I'll show both.

 

public ReturnType1 Method1(long barcode)
{
    try
    {
        DataProvider dp = new DataProvider();
        return dp.Method1();
    }
    catch (ApplicationException aex)
    {
        LogWriter.LogException(aex);
        throw new Exception(aex.Message);
    }
    catch (Exception ex)
    {
        LogWriter.LogException(ex);
        throw new Exception("An unhandled server exception was encountered.");
    }
}

public void Method2(long id)
{
    try
    {
        DataProvider dp = new DataProvider();
        dp.Method2(id);
    }
    catch (ApplicationException aex)
    {
        LogWriter.LogException(aex);
        throw new Exception(aex.Message);
    }
    catch (Exception ex)
    {
        LogWriter.LogException(ex);
        throw new Exception("An unhandled server exception was encountered.");
    }
}

 

You see that in the two methods above, the entire method body is identical except for the content in the actual try { } block.  That much repetition is verbose, error-prone, harder to read and unnecessary.  It's easy to see how several WCF service calls would end up looking very repetetive too. 

 

public void CallWCFMethod()
{
    ServiceClient client = new ServiceClient();
    try
    {
        try { 
            client.Method1(); 
        } catch Exception(ex) {
            LogError(ex); 
        }
    }
    finally
    {
        try
        {
            client.Close();
            if (client is IDisposable)
                ((IDisposable)client).Dispose();
        }
        catch { Log("WCF connection closed in a faulted state."); }
    }
}

I realize that many of you will want to argue that WCF client should be instantiated in a Using block.  Some others will say that the dispose method will call the Close() method. 

In the code above, the only line that differs between methods is the line that actually calls client.Method1();  All other lines would be present in every method that consume that WCF service.  There is a lot of wasted code here. 

The common thread in these two scenarios is that the functional part of each method differs, but the fixture is the same.  The fixture is the code that runs before and after the functional code to set up in preparation for the call and to clean up after the call.  This can be done using a callback. 

To solve this, we need a method that contains the fixture, which we need only write once.  Inside the fixture, a callback method will be used to execute code you specify.  That code can be anything and is determined at the time of the call.  The fixture method doesn't have to know what it is.  Using generics and anonymous methods, this syntax is also reasonably readable.

 

Consider this solution for the first scenario, the exception handlers.

 

public TResult HandleExceptions<TResult>(Func<TResult> operationCallback)
{
    try
    {
        return operationCallback();
    }
    catch (ApplicationException aex)
    {
        LogWriter.LogException(aex);
        throw new Exception(aex.Message);
    }
    catch (Exception ex)
    {
        LogWriter.LogException(ex);
        throw new Exception("An unhandled server exception was encountered.");
    }
}

 

Notice that the only parameter for this method is a callback (also known by many other names- function pointer, delegate, etc.).  The point is that you pass it in and it is called inside the try { } block.  To consume this fuxture method, you just pass your functional code (which used to be inside the try { }  block) as a parameter.

 

public ReturnType1 Method1(string itemCode)
{
    return HandleExceptions(
        delegate()
        {
            DataProvider dp = new DataProvider();
            return dp.Method1;
        }
    );
}
public void Method2(string itemCode)
{
    HandleExceptions(
        delegate()
        {
            DataProvider dp = new DataProvider();
            dp.Method2;
            return 0;
        }
    );
}

 

For those unfamiliar with this syntax, the delegate() { } part says that the code between curly braces { } is a method that will be passed as a parameter to HandleExceptions.  In a strict world, I would have to specify the return type as delegate<ReturnType1> or delegate<int>().  Likewise, the Method1<ReturnType> and Method2<int> declarations would be required with type parameters. 

As it is, however, our friend Type Inference determines what those type parameters should be and renders them optional.  This is not only nice in a time-saving capacity, but in other situations may also lead to further code reuse. 

 

Here is an example of a provision of reusable fixture for the aforementioned WCF call.

 

public TResult CallWCFMethod<TResult>(Func<ServiceClient, TResult> operationCallback)
{
    ServiceClient client = new ServiceClient;
    try
    {
        try { 
            return operationCallback(client);
        } catch Exception(ex) {
            LogError(ex); 
        }

    }
    finally
    {
        try
        {
            client.Close();
            if (client is IDisposable)
                ((IDisposable)client).Dispose();
        }
        catch { }
    }
}

 

All we've done is coded the input func<> parameter with the type of the parameter that will be passed to operationCallback, and then changed the functional try { } block to call the operationCallback function, passing the service client object as a parameter.  You can easily put any additional logging, setup or teardown requirements for all WCF calls in just one place.  It is now a simple matter to call any method in the WCF service without constantly opening and closing the connection each time, without the ridiculous exception handling that WCF calls require, and without incessant logging calls.  Subsequent calls are all very terse and easy to understand.  They look like this:

 

public void WCFMethod1()
{
    CallWCFMethod(
        delegate(ServiceClient client)
        {
            client.Method1();
            return 0;
        }
    );
}

 

Of course, the above example is for a void call.  Any call that actually returns something can have the return type inferred and returned as the result of the CallWCFMethod method as already shown in the exception handler example above and in the following example.

 

 

public ReturnType1 WCFMethod2()
{
    return CallWCFMethod(
        delegate(ServiceClient client)
        {
            return client.Method2();
        }
    );
}

 

 

 Phil Gilmore ( www.interactiveasp.net )