3

さまざまなサーバーに接続してデータを読み取り、操作を実行するソリューションを開発しています。ファイアウォール、停止/失敗したサービス、認証の違い、さまざまなソフトウェア構成など、信頼できる通信を複雑にする多くの変数があります。これらの問題を回避するために使用できる方法がありますが、実行時にはどれが成功するかはわかりません。

私の目標は、操作を実行するために使用できるインターフェイスと実装を作成することです。最初のメソッド呼び出しは、ほとんどのデバイスで機能する最速の実装に対するものであり、その後に、前述の問題に対処できる他の呼び出しが続きます。

完璧な世界では、プロセスはどのメソッドが成功するかをすばやく特定するように記述されますが、私のテストでは、単純に例外をキャッチするのと同じくらい多くの処理時間がかかりました。パフォーマンスは常に考慮事項ですが、最終的には、タスクが正常に完了することがより重要です。

以下は私が作成した例で、実装のリストを繰り返し処理する最悪のシナリオを示しています。これは 1 つの方法ではうまく機能しますが、20 以上の異なる操作で使用される場合、DRY の原則には従いません。考えられる解決策の 1 つは 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;
    }
}

特別な実装は、1 つが例外なく成功するまで、他の実装をループします。

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

3 に答える 3

1

単一のアイテムの処理を行うメソッドを渡す汎用拡張メソッドとして処理操作を実装できます。

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));
于 2011-12-11T19:46:08.043 に答える
0

2 番目のインターフェイスで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;
    }
}
于 2011-12-11T19:39:36.567 に答える
0

非常にシンプルで「一般的な」ものが必要な場合は、@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));
于 2011-12-11T20:13:27.730 に答える