I have a “rest client” that wraps HttpClient and whose methods are async.
Besides other reasons, I need to control signin/signout process with my rest client so that number of sessions is not exceeded.
The rest client implements IDisposable and upon disposing the client I need to check if the client is “still signed in” and sign out if it is.
Since doing any kind of external calls in Dispose method is considered bad practice, I have something as following
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();
}
}
}
}
The use of
.ConfigureAwait(false)is a non-issue. You aren’t awaiting on the task at all. Since you don’tawaitit, it doesn’t matter whatawaitis configured to do.What you’re doing is just basic fire and forget (which may or may not be acceptable for you).
You should remove the
ConfigureAwait(false)no matter what, just because it does nothing and is confusing to the reader. If it’s okay for you to send the request to sign out but not actually sign out, then this is okay.If you need to ensure that
restClient.Dispose();isn’t called until the sign out request returns, then you have a bit of a…problem. The problem stems from the fact that the sign out request might be unsuccessful, or much worse, it might not respond at all. You’d need some way of dealing with that.You can’t use
awaitin afinallyblock, but you can more or less mimic its behavior through continuations. You may need to do something like this:Note that since you aren’t using
awaitthe task returned fromDoStuffwill indicate that it is “done” as soon as it hits the finally block for the first time; not when the continuation fires and the object is disposed. That may or may not be acceptable.