Несколько попыток реализации - PullRequest
3 голосов
/ 11 декабря 2011

Я разрабатываю решение, которое будет подключаться к широкому кругу серверов для чтения данных и выполнения операций. Существует много переменных, которые усложняют надежную связь, такие как брандмауэры, остановленные / отказавшие службы, различия в аутентификации и различные конфигурации программного обеспечения. Существуют методы, которые я могу использовать, чтобы обойти эти проблемы, хотя во время выполнения неизвестно, что окажется успешным.

Моя цель - создать интерфейс и реализации, которые можно использовать для выполнения операций. Первый вызов метода будет самой быстрой реализацией, которая работает для большинства устройств, за которой следуют другие вызовы, которые могут решить проблемы, перечисленные ранее.

В идеальном мире был бы написан процесс, позволяющий быстро определить, какой метод будет успешным, но в моих тестах это занимало столько же времени обработки, сколько простое обнаружение исключения. Хотя производительность всегда учитывается, в конце концов, более важно, чтобы задача была успешно завершена.

Ниже приведен пример, который я создал, который демонстрирует наихудший сценарий, повторяющийся в списке реализаций. Хотя это хорошо работает для одного метода, он не следует принципу СУХОГО при использовании в 20 или более различных операциях. Одним из возможных решений является Unity и Interception, но я обнаружил, что метод invoke в обработчике вызовов использует разрешенную реализацию, а не список возможных реализаций. Если я что-то упустил, это не вариант. Кроме того, мне нужно будет следовать этому шаблону для нескольких интерфейсов, поэтому было бы неплохо создать универсальный обработчик, который может перебирать список реализаций.

Любой совет о том, как выполнить эту задачу, будет принят!

Интерфейс

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

Реализация

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;
    }
}

Специальная реализация перебирает другие реализации, пока одна из них не завершится успешно

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;
        }
    }
}

Ответы [ 3 ]

1 голос
/ 11 декабря 2011

Вы можете реализовать операцию обработки в качестве универсального метода расширения, передавая метод, который выполняет обработку для одного элемента:

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;
}

Теперь вы вычли фактический тип элемента и чтометод, который вы используете для обработки.Единственное, что «исправлено» - это тип результата универсального метода, который является целым числом.

Для вашего текущего примера вы можете вызвать его так:

List<IProcess> implementations = ...;
int processResult = items.ProcessItems(x => x.ProcessItem(workType));
0 голосов
/ 12 декабря 2011

Вот решение, похожее на решение, созданное @BrokenGlass, если вы хотите что-то очень простое и «универсальное».

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.");
}

Поскольку я вижу тег Unity, вы можете использовать ResolveAll<TService>() в своем контейнере, используя интерфейс службы, чтобы получить все реализации. Комбинируя это с этим кодом, вы можете сделать что-то вроде метода расширения на 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.");
    }
}

Вот как это может работать:

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));
0 голосов
/ 11 декабря 2011

Вы можете использовать шаблон TryParse во втором интерфейсе:

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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...