1

HttpClientラップし、メソッドが非同期の「残りのクライアント」があります。他の理由に加えて、セッション数を超えないように、残りのクライアントでサインイン/サインアウト プロセスを制御する必要があります。

残りのクライアントが実装されIDisposable、クライアントを破棄するときに、クライアントが「まだサインイン」しているかどうかを確認し、サインアウトしている場合はサインアウトする必要があります。Dispose メソッドであらゆる種類の外部呼び出しを行うことは悪い習慣と見なされるため、次のようなものがあります。

public class MappingsController : RestController
{
    [HttpGet]
    public async Task<HttpResponseMessage> GetYears()
    {
        return await ProcessRestCall(async rc => await rc.GetYearsAsync());
    }
}

public class RestController : ApiController
{
    protected async Task<HttpResponseMessage> ProcessRestCall<T>(Func<RestClient, Task<T>> restClientCallback)
    {
        RestClient restClient = null;
        try
        {
            var credentials = GetCredentialsFromRequestHeader();
            if (credentials == null)
            {
                return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Missing credentials from header!");
            }
            var username = credentials["Username"];
            var password = credentials["Password"];

            restClient = new RestClient(username, password);
            var authenticated = await restClient.SignInAsync();
            if (!authenticated)
            {
                return CreateErrorResponseWithRestStatus(HttpStatusCode.Unauthorized, restClient);
            }
            var result = await restClientCallback(restClient);
            // Following works, but since I need to do it in finally block in case exception happens, perhaps It should be done in finally anyways...
            //await restClient.SignOutAsync(); 
            var response = Request.CreateResponse(HttpStatusCode.OK, result);
            return response;
        }
        catch (Exception e)
        {
            return CreateErrorResponseWithRestStatus(HttpStatusCode.BadRequest, restClient, e);
        }
        finally
        {
            if (restClient != null)
            {
                if (restClient.IsSignedIn)
                {
                    //var signedOutOk = restClient.SignOutAsync();//.Result; //<-- problem - this blocks!!!
                    restClient.SignOutAsync().ConfigureAwait(false); // seems to work, but I am not sure if this is kosher + I can't get return var

                    //Logger.Warn(CultureInfo.InvariantCulture, m => m("Client was still signed in! Attempt to to sign out was {0}", signedOutOk ? "successful" : "unsuccessful"));
                }
                restClient.Dispose();
            }
        }
    }
}
4

1 に答える 1

4

の使用は.ConfigureAwait(false)問題ではありません。 あなたはその仕事をまったく待っていません。 そうしないawaitので、何awaitをするように構成されているかは問題ではありません。

あなたがやっていることは、基本的な火事や忘れ物です (これはあなたにとって受け入れられるかもしれませんし、受け入れられないかもしれません)。

ConfigureAwait(false)何もせず、読者を混乱させるという理由だけで、を削除する必要があります。サインアウトの要求を送信しても実際にはサインアウトしない場合は、これで問題ありません。

restClient.Dispose();サインアウト要求が返されるまで呼び出されないようにする必要がある場合は、少し問題があります。この問題は、サインアウト要求が失敗するか、さらに悪いことに、まったく応答しない可能性があるという事実に起因しています。それに対処する方法が必要です。

awaitブロック内で使用することはできませんがfinally、継続によってその動作を多かれ少なかれ模倣することができます。次のようなことをする必要があるかもしれません:

public static async Task DoStuff()
{
    IDisposable disposable = null;
    try { }
    finally
    {
        var task = GenerateTask();
        var continuation = Task.WhenAny(task, Task.Delay(5000))
            .ContinueWith(t =>
            {
                if (task.IsCompleted) //if false we timed out or it threw an exception
                {
                    var result = task.Result;
                    //TODO use result
                }

                disposable.Dispose();
            });
    }
}

awaitから返されたタスクを使用していないDoStuffため、finally ブロックに初めて到達するとすぐに「完了」したことが示されることに注意してください。継続が発火し、オブジェクトが破棄されたときではありません。それは受け入れられるかもしれないし、受け入れられないかもしれません。

于 2013-01-31T22:01:53.170 に答える