2013-01-31 31 views
1

我有一个“休息客户端”,包装HttpClient和其方法是异步的。 除了其他原因外,我需要使用我的休息客户端控制登录/注销过程,以便不超过会话数量。在finally块中调用.ConfigureAwait(false)是否安全?

其余客户端实现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

使用的.ConfigureAwait(false)是一个不是问题的问题。 你根本就没有在等待任务。因为你不是await它,await配置要做什么并不重要。

你在做什么只是基本的火灾和遗忘(这可能会或可能不会被你接受)。

无论如何,你应该删除ConfigureAwait(false),只是因为它什么都不做,并且让读者感到困惑。如果您可以发送请求注销但没有实际注销,那么这没问题。

如果您需要确保restClient.Dispose();在注销请求返回之前未被调用,那么您有一点问题。问题源于签出请求可能不成功,或者更糟糕的是,它可能根本没有响应。你需要一些处理方法。

您不能在finally块中使用await,但您可以通过延续或多或少地模仿其行为。你可能需要做这样的事情:

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块的第一次;而不是当连续火灾和物体丢弃时。这可能会也可能不会被接受。

+0

您已正确识别我的需求。在我处理之前,我需要检查客户端是否仍然登录,如果是,那么我需要*至少*尝试登出。例如,尝试退出并等待5秒钟,然后进行处理...并另外捕获退出尝试的返回值......自等待以来非常棘手的问题无法进入终止块,这对于处置调用来说是“完美的” :) – zam6ak

+0

@ zam6ak你基本上必须做什么'await',但没有'await'。在退出方法上调用'ContinueWith',传递取消标记或使用其他一些提供超时的机制,然后处理客户端并执行......无论如何...以及在该延续内注销的结果。 – Servy

+0

@ zam6ak请参阅编辑示例。 – Servy

相关问题