多次实施尝试

发布于 2024-12-20 22:21:02 字数 2556 浏览 5 评论 0原文

我正在开发一个解决方案,该解决方案将连接到各种服务器以读取数据并执行操作。有许多变量使可靠通信变得复杂,例如防火墙、停止/失败的服务、身份验证差异和各种软件配置。我可以使用一些方法来解决这些问题,尽管在执行时尚不知道哪种方法会成功。

我的目标是创建一个可用于执行操作的接口和实现。第一个方法调用将是适用于大多数设备的最快实现,然后是可以处理前面列出的问题的其他调用。

在完美的世界中,该过程将被编写为快速识别哪种方法会成功,但在我的测试中,花费的处理时间与简单捕获异常一样多。虽然性能始终是一个考虑因素,但最终成功完成任务更为重要。

下面是我创建的一个示例,它演示了迭代实现列表的最坏情况场景。虽然这对于一种方法效果很好,但当用于 20 种或更多不同的操作时,它不遵循 DRY 原则。一种可能的解决方案是 Unity 和 Interception,但我发现调用处理程序中的调用方法使用已解析的实现,而不是可能的实现列表。除非我错过了什么,否则这似乎不是一个选择。另外,我需要对多个接口遵循此模式,因此最好创建一个可以迭代实现列表的通用处理程序。

任何有关如何完成此任务的建议将不胜感激!

接口

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

失眠症患者 2024-12-27 22:21:02

您可以将处理操作实现为通用扩展方法,您传递一个对单个项目进行处理的方法:

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

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));
失退 2024-12-27 22:21:02

您可以在第二个界面中使用 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;
    }
}

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;
    }
}
煞人兵器 2024-12-27 22:21:02

如果您想要非常简单和“通用”的东西,这里有一个类似于@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() 来获取所有实现。将此代码与此代码相结合,您可以在 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));

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));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文