如何恰当地确定等待方法的异常

时间:2018-05-29 00:09:25

标签: c# asp.net-mvc entity-framework async-await

我花了好几个小时试图搞清楚(标题)。所以我有两种方法:

public virtual TEntity GetByName(Expression<Func<TEntity, bool>> whereName)
    {
        return this.DbEntitySet.FirstOrDefault(whereName);;
    } 

public virtual async Task<TEntity> GetByNameAsync(Expression<Func<TEntity, bool>> whereName)
    {
        try
        {                
            return await this.DbEntitySet.FirstOrDefaultAsync(whereName);
        }
        catch (AggregateException ae)
        {
            throw;
        }
        catch (Exception ex)
        {

            throw;
        }
    }  

第一种方法按预期工作,但第二种方法如果找到则运行正常但是 进入一个永无止境的过程,如果不是。我希望抛出异常(可能通过AggregateException),即使它没有抛出TimeOutException。我试着修改它:

public virtual async Task<TEntity> GetByNameAsync(Expression<Func<TEntity, bool>> whereName)
    {
        try
        {
            TimeSpan ts = TimeSpan.FromMilliseconds(5000);
            Task<TEntity> task = this.DbEntitySet.FirstOrDefaultAsync(whereName);
            if (!task.Wait(ts))
            {
                throw new TimeoutException();
            }
            return await task;
        }
        catch (AggregateException ae)
        {
            throw;
        }
        catch (Exception ex)
        {

            throw;
        }
    }

然后此方法运行正常WITHOUT ANY exception。 请帮忙吗?

这就是我如何称呼这种方法(从开始)

//1
var activityManager = HttpContext.Current.GetOwinContext().Get<ActivityManager<ActivityModels>>();
activityManager.CreateAsync(model).GetAwaiter().GetResult();
//2
public virtual async Task<WebCoreResult> CreateAsync(TActivity activity)
    {
        this.ThrowIfDisposed();

        if ((object)activity == null || string.IsNullOrEmpty(activity.ActivityName))
            throw new ArgumentNullException(nameof(activity));
        try
        {

            WebCoreResult result = await ActivityExistAsync(activity);
            if (result.Succeeded)
                return result;
        }
        catch (Exception)
        {

            throw;
        }
        //code removed for brevity                
    }
//3
public async Task<WebCoreResult> ActivityExistAsync(TActivity activity)
    {
        var a = await FindByNameAsync(activity.ActivityName);
        //var a = FindByName(activity.ActivityName);    //this one ok

        if (a == null)
        {
            return await Task.FromResult(new WebCoreResult("Activity not exist"));
        }
        return await Task.FromResult(WebCoreResult.Success);
    }
//4
public virtual async Task<TActivity> FindByNameAsync(string activityName)
    {
        this.ThrowIfDisposed();
        if (activityName == null)
            throw new ArgumentNullException(nameof(activityName));
        return (TActivity)await this._activityStore.FindByNameAsync(activityName);
    }
//5
public async Task<ActivityModels> FindByNameAsync(string name)
    {
        this.ThrowIfDisposed();
        if (string.IsNullOrWhiteSpace(name))
        {
            throw new ArgumentNullException("activityname");
        }
        return await _activityStore.GetByNameAsync(x => x.ActivityName.ToLower() == name.ToLower());
    }
//6 finale: GetByNameAsync method above

1 个答案:

答案 0 :(得分:1)

这个问题可能是由这一行导致的:

activityManager.CreateAsync(model).GetAwaiter().GetResult()

GetResult的调用强制线程阻塞,直到Awaiter完成。所以你阻止了ASP.NET的主循环。一旦await尝试继续执行,问题就会发生。此时,它将安排连接到ASP.NET主线程的SynchronizationContext中的延续。但是这个延续从未执行过,因为该线程被阻止为GetResult

这就是你可能造成僵局的原因。

有一些方法可以解决这个问题。

  1. 请勿将awaitWait()GetResult()混在一起。如果你只使用其中之一,你会没事的。
  2. 如果您需要阻止可能具有SynchronizationContext的线程,则需要确保使用await的函数不会尝试恢复此上下文。最简单的方法是将初始调用包装成Task.Run(() => ...)。那可行。 通过查询SynchronizationContext,您可以查看当前线程中是否有System.Threading.SynchronizationContext.Current处于活动状态。如果是nullSystem.Threading.SynchronizationContext的实例你没事。如果是任何特殊实现,您需要注意。
  3. 您需要订购await次来忽略当前SynchronizationContext。这可以通过在等待的对象上调用ConfigureAwait(false)来完成。但是,至少在每个涉及的方法中,每个await都需要这样做。
  4. 这些方法中的任何一种都可以解决问题。