我有一个“休息客户端”包装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(); } } } }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(); } } } }最满意答案
使用.ConfigureAwait(false)是一个非问题。 你根本没有等待任务。 由于您没有await它,因此await配置要做什么并不重要。
你正在做的只是基本的火灾和忘记(你可能接受或不接受)。
您应该删除ConfigureAwait(false)无论如何,只是因为它什么都不做并且让读者感到困惑。 如果您可以发送退出请求但实际上没有注销,那么这没关系。
如果需要确保restClient.Dispose(); 在签出请求返回之前不会被调用,那么你有一点......问题。 问题源于退出请求可能不成功,或者更糟糕的是,它可能根本不响应。 你需要一些方法来解决这个问题。
你不能在finally块中使用await ,但你可以通过continuation来或多或少地模仿它的行为。 您可能需要执行以下操作:
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块时立即指示它已“完成”; 不是当继续射击并且物体被丢弃时。 这可能是也可能是不可接受的。
The use of .ConfigureAwait(false) is a non-issue. You aren't awaiting on the task at all. Since you don't await it, it doesn't matter what await is 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 await in a finally block, but you can more or less mimic its behavior through continuations. You may need to do something like this:
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(); }); } }Note that since you aren't using await the task returned from DoStuff will 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.
更多推荐
发布评论