3

I am developing a solution which will connect to a wide variety of servers to read data and perform operations. There are many variables which complicate reliable communications such as firewalls, stopped/failed services, authentication differences, and various software configurations. There are methods I can use to work around these issues, though at the time of execution it is not known which will prove successful.

My goal is to create an interface and implementations which can be used to perform operations. The first method call will be to the fastest implementation which works for the majority of devices followed by other calls which can deal with the issues listed earlier.

In a perfect world the process would be written to quickly identify which method would be successful, but in my tests that took as much processing time as simply catching an exception. While performance is always a consideration, in the end it is more important that the task completes successfully.

Below is an example I created which demonstrates a worst case scenario iterating over a list of implementations. While this works well for one method, it doesn't follow the DRY principle when used in 20 or more different operations. One possible solution is Unity and Interception but I found that the invoke method in the call handler uses a resolved implementation, not a list of possible implementations. Unless I am missing something, that doesn't appear to be an option. Also, I will need to follow this pattern for several interfaces, so it would be nice to create a generic handler which can iterate over a list of implementations.

Any advice on how to complete this task would be appreciated!

Interface

public interface IProcess
{
    int ProcessItem(string workType);
}

Implementations

public class ProcessImplementation1 : IProcess
{
    public int ProcessItem(string workType)
    {
        throw new TimeoutException("Took too long");
    }
}

public class ProcessImplementation2 : IProcess
{
    public int ProcessItem(string workType)
    {
        throw new Exception("Unexpected issue");
    }
}

public class ProcessImplementation3 : IProcess
{
    public int ProcessItem(string workType)
    {
        return 123;
    }
}

Special Implementation loops through the other implementations until one succeeds without exception

public class ProcessImplementation : IProcess
{
    public int ProcessItem(string workType)
    {
        List<IProcess> Implementations = new List<IProcess>();
        Implementations.Add(new ProcessImplementation1());
        Implementations.Add(new ProcessImplementation2());
        Implementations.Add(new ProcessImplementation3());
        int ProcessId = -1;
        foreach (IProcess CurrentImplementation in Implementations)
        {
            Console.WriteLine("Attempt using {0} with workType '{1}'...",
                CurrentImplementation.GetType().Name, workType);
            try
            {
                ProcessId = CurrentImplementation.ProcessItem(workType);
                break;
            }
            catch (Exception ex)
            {
                Console.WriteLine("  Failed: {0} - {1}.",
                    ex.GetType(), ex.Message);
            }
            Console.WriteLine();

            if (ProcessId > -1)
            {
                Console.WriteLine("  Success: ProcessId {0}.", ProcessId);
            }
            else
            {
                Console.WriteLine("Failed!");
            }
            return ProcessId;
        }
    }
}
Robert
  • 95
  • 2
  • 8
  • I was going to suggest the Composite pattern, when on closer reading I realized you'd already implemented it. – TrueWill Dec 11 '11 at 19:41
  • @Maxim - why did you delete your Chain-of-responsibility pattern answer? I was going to upvote that! – TrueWill Dec 11 '11 at 20:05

3 Answers3

1

You could implement the processing operation as a generic extension method that you pass a method that does the processing for a single item:

public static int ProcessItems<T>(this IEnumerable<T> items, Func<T, int> processMethod)
{
    foreach (var item in items)
    {
        try
        {
            return processMethod(item);
        }
        catch(Exception) {}
    }
    return -1;
}

Now you have factored out the actual type of the item and what method you use for processing. The only thing "fixed" is the result type of the generic method which is an integer.

For your current example you could call it like this then:

List<IProcess> implementations = ...;
int processResult = items.ProcessItems(x => x.ProcessItem(workType));
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
0

You could use the TryParse pattern in a second interface:

public interface IProcess
{
    int ProcessItem(string workType);
}

internal interface ITryProcess
{
    bool TryProcessItem(string workType, out int result);
}

public class ProcessImplementation1 : ITryProcess
{
    public bool TryProcessItem(string workType, out int result)
    {
        result = -1;
        return false;
    }
}

public class ProcessImplementation : IProcess
{
    public int ProcessItem(string workType)
    {
        var implementations = new List<ITryProcess>();
        implementations.Add(new ProcessImplementation1());
        // ...
        int processId = -1;
        foreach (ITryProcess implementation in implementations)
        {
            if (implementation.TryProcessItem(workType, out processId))
            {
                break;
            }
        }
        if (processId < 0)
        {
            throw new InvalidOperationException("Unable to process.");
        }
        return processId;
    }
}
TrueWill
  • 25,132
  • 10
  • 101
  • 150
  • 1
    I ended up going this route. While this is not the most DRY solution proposed, it does address an issue I was staring to have with a lot of exception handling throughout my solution. – Robert Dec 20 '11 at 18:05
0

Here's a solution similar to the one created by @BrokenGlass if you want something very simple and "generic."

public void TryAllImplementations<TService>(
    IEnumerable<TService> services,
    Action<TService> operation,
    Action<Exception> exceptionHandler = null)
{
    int dummy = 0;
    TryAllImplementations(
        services, 
        svc => { operation(svc); return dummy; },
        exceptionHandler);
}

public TReturn TryAllImplementations<TService, TReturn>(
    IEnumerable<TService> services, 
    Func<TService, TReturn> operation,
    Action<Exception> exceptionHandler = null)
{
    foreach (var svc in services)
    {
        try
        {
            return operation(svc);
        }
        catch (Exception ex)
        {
            if (exceptionHandler != null)
                exceptionHandler(ex);
        }
    }
    throw new ProgramException("All implementations have failed.");
}

Since I see a Unity tag, you could use ResolveAll<TService>() on your container using your service interface to get all implementations. Combining that with this code, you could do something like an extension method on IUnityContainer:

public static class UnityContainerExtensions
{
    public static void TryAllImplementations<TService>(
        this IUnityContainer container,
        Action<TService> operation,
        Action<Exception> exceptionHandler = null)
    {
        int dummy = 0;
        container.TryAllImplementations<TService, int>(
            svc => { operation(svc); return dummy; },
            exceptionHandler);
    }

    public static TReturn TryAllImplementations<TService, TReturn>(
        this IUnityContainer container,
        Func<TService, TReturn> operation,
        Action<Exception> exceptionHandler = null)
    {
        foreach (var svc in container.ResolveAll<TService>())
        {
            try
            {
                return operation(svc);
            }
            catch (Exception ex)
            {
                if (exceptionHandler != null)
                    exceptionHandler(ex);
            }
        }
        throw new ProgramException("All implementations have failed.");
    }
}

Here's how using it could work:

IUnityContainer container;

// ...

container.RegisterType<IProcess, ProcessImplementation1>();
container.RegisterType<IProcess, ProcessImplementation2>();
container.RegisterType<IProcess, ProcessImplementation3>();

// ...

container.TryAllImplementations(
    (IProcess svc) => svc.ProcessItem(workType),
    ex => Console.WriteLine(
        "  Failed: {0} - {1}.",
        ex.GetType(), 
        ex.Message));
Jacob
  • 77,566
  • 24
  • 149
  • 228