5

私は DRY コーディングの大ファンで、定型コードはできるだけ避けたいと思っています。したがって、すべての WCF チャネル faff を、WCF チャネルのライフサイクルを処理する AOP クラスにリファクタリングしました。

また、私は async-await の大ファンでもあり、特に WCF では、通常はスリープ状態で応答を待機しているスレッドを理論的に解放できるためです。

そこで、fluentAOP lib にインターセプターを作成しました

    private static object InvokeOnChannel(IMethodInvocation methodInvocation)
    {
        var proxy = _factory.CreateChannel();
        var channel = (IChannel) proxy;
        try
        {
            channel.Open();
            var ret = methodInvocation.Method.Invoke(proxy, methodInvocation.Arguments);
            channel.Close();
            return ret;
        }
        catch (FaultException ex)
        {
            if (ex.InnerException != null)
                throw ex.InnerException;
            throw;
        }
        catch(Exception)
        {
            channel.Abort();
            throw;
        }
    }

ただし、解決策について少し考えてみると、フォームの WCF コントラクトの場合、

[ServiceContract]
public interface IFoo
{
    [OperationContract]
    Task<int> GetInt();
}

GetInt は予期しない結果をもたらします。まず、catch FaultException は何もしません。次に、リクエストが返される前にチャネルを閉じます。戻り値の型が Task の場合、理論的には別のコード パスに切り替えることができます。しかし、Task<> の結果を待ってから awaitable を返す方法がわかりません。

もちろん、これは特に困難です。なぜなら、ランタイム AOP を使用すると、戻り値の型のジェネリックを使用できるアクセス権がないからです (リフレクションの全体がなければ)。

この関数を awaitable として実装する方法はありますか?完了時にチャネルを閉じ、呼び出しスレッドへの例外をキャッチ/マーシャリングしますか?

4

1 に答える 1

7

インジェクションを行うasyncには、返されたタスクを置き換える必要があります。asyncコードを読みやすくするために、 を使用する代わりにメソッドに置き換えることをお勧めしContinueWithます。

fluentAOP には詳しくありませんがasync、Castle DynamicProxy でインジェクションを実行しました。

リフレクションを使用する場合は、最初にそれがasync呼び出しであるかどうか (つまり、戻り値の型が のサブクラスであるか、または等しいかどうか) を判断しtypeof(Task)ます。async呼び出しの場合は、リフレクションを使用する必要があります。から引き出してTTask<T>独自のasyncメソッドに適用します。

private static MethodInfo handleAsync = ...; // point this to HandleAsync<T>

// Only called if the return type is Task/Task<T>
private static object InvokeAsyncOnChannel(IMethodInvocation methodInvocation)
{
    var proxy = _factory.CreateChannel();
    var channel = (IChannel) proxy;
    try
    {
        channel.Open();
        var task = methodInvocation.Method.Invoke(proxy, methodInvocation.Arguments) as Task;
        object ret;
        if (task.GetType() == typeof(Task))
            ret = HandleAsync(task, channel);
        else
            ret = handleAsync.MakeGenericMethod(task.GetType().GetGenericParameters()).Invoke(this, task, channel);
        return ret;
    }
    catch (FaultException ex)
    {
        if (ex.InnerException != null)
            throw ex.InnerException;
        throw;
    }
    catch(Exception)
    {
        channel.Abort();
        throw;
    }
}

private static async Task HandleAsync(Task task, IChannel channel)
{
    try
    {
        await task;
        channel.Close();
    }
    catch (FaultException ex)
    {
        if (ex.InnerException != null)
            throw ex.InnerException;
        throw;
    }
    catch(Exception)
    {
        channel.Abort();
        throw;
    }
}

private static async Task<T> HandleAsync<T>(Task task, IChannel channel)
{
    try
    {
        var ret = await (Task<T>)task;
        channel.Close();
        return ret;
    }
    catch (FaultException ex)
    {
        if (ex.InnerException != null)
            throw ex.InnerException;
        throw;
    }
    catch(Exception)
    {
        channel.Abort();
        throw;
    }
}

別の方法は、次を使用することdynamicです。

private static object InvokeOnChannel(IMethodInvocation methodInvocation)
{
    var proxy = _factory.CreateChannel();
    var channel = (IChannel) proxy;
    try
    {
        channel.Open();
        dynamic result = methodInvocation.Method.Invoke(proxy, methodInvocation.Arguments);
        return Handle(result, channel);
    }
    catch (FaultException ex)
    {
        if (ex.InnerException != null)
            throw ex.InnerException;
        throw;
    }
    catch(Exception)
    {
        channel.Abort();
        throw;
    }
}

private static async Task Handle(Task task, IChannel channel)
{
    try
    {
        await task;
        channel.Close();
    }
    catch (FaultException ex)
    {
        if (ex.InnerException != null)
            throw ex.InnerException;
        throw;
    }
    catch(Exception)
    {
        channel.Abort();
        throw;
    }
}

private static async Task<T> Handle<T>(Task<T> task, IChannel channel)
{
    await Handle((Task)task, channel);
    return await task;
}

private static T Handle<T>(T result, IChannel channel)
{
    channel.Close();
    return result;
}
于 2013-03-04T13:56:43.490 に答える