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 回答