我有一个“rest client”包装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();
}
}
}
}
答案 0 :(得分: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块时立即显示“已完成”;不是当连续射击并且物体被丢弃时。这可能是也可能是不可接受的。