ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

与MS AsyncHelper同步运行异步方法的空异常

2019-10-28 17:18:26  阅读:256  来源: 互联网

标签:razor asynchronous synchronous c asp-net-mvc


由于您无法通过子窗口(@ Html.Action)调用运行异步方法,因此我一直在寻找从非异步方法运行异步任务的最简单方法.这样,我的MainMenu控制器的Menu动作在注入后仍然可以正常工作(而不是必须迁移到VM或Ajax解决方案):

<div class="row">
    @Html.Action("MainMenu", "Menu")
</div>

我使用MS自己使用的代码副本尝试了这种有前途的方法:How to call asynchronous method from synchronous method in C#?

AsyncHelper代码:

public static class AsyncHelper
{
    private static readonly TaskFactory _myTaskFactory = new
          TaskFactory(CancellationToken.None,
                      TaskCreationOptions.None,
                      TaskContinuationOptions.None,
                      TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        return AsyncHelper._myTaskFactory
          .StartNew<Task<TResult>>(func)
          .Unwrap<TResult>()
          .GetAwaiter()
          .GetResult();
    }

    public static void RunSync(Func<Task> func)
    {
        AsyncHelper._myTaskFactory
          .StartNew<Task>(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

我正在像这样消费它:

    public async Task<ActionResult> MainMenu()
    {
        if (_currentCandidate == null)
        {
            throw new ArgumentNullException("_currentCandidate");
        }
        var candidateId = AsyncHelper.RunSync<int>(() => _currentCandidate.CandidateIdAsync());

        [snip]
    }

哪个调用此异步方法:

    public async Task<int> CandidateIdAsync()
    {
        var applicationUser = await this.ApplicationUserAsync();
        if (applicationUser != null)
        {
            return applicationUser.CandidateId.GetValueOrDefault();
        }
        return 0;
    }

仅当我运行此命令时,才会出现以下错误:

我在这里想念什么?该代码看起来应该可以工作,但是我对它还不很熟悉,因此无法弄清楚.

更新:

供参考,MainMenu控制器类如下所示:

public class MenuController : Controller
{
    readonly ICurrentCandidate _currentCandidate;

    public MenuController(ICurrentCandidate currentCandidate)
    {
        _currentCandidate = currentCandidate;
    }

    // GET: MainMenu
    public ActionResult MainMenu()
    {
        if (_currentCandidate == null)
        {
            throw new ArgumentNullException("_currentCandidate");
        }
        var candidateId = AsyncHelper.RunSync<int>(() => _currentCandidate.CandidateIdAsync());

        [snip]            
        return View(vm);
    }
}

另一个更新:

由于简化的CandidateIdAsnyc的工作,该故障似乎在相关的IF代码内部:

// This works
public async Task<int> CandidateIdAsync()
{
    return 0;
}

这是该代码的其余部分:

public class CurrentCandidate : ICurrentCandidate
{
    private readonly ApplicationDbContext _applicationDbContext;
    private readonly IApplicationUserManager _userManager;
    private readonly ICandidateStore _candidateStore;

    public CurrentCandidate(ApplicationDbContext applicationDbContext, ICandidateStore candidateStore, IApplicationUserManager userManager)
    {
        this._candidateStore = candidateStore;
        this._applicationDbContext = applicationDbContext;
        this._userManager = userManager;    // new ApplicationUserManager(new UserStore<ApplicationUser>(this._applicationDbContext));
    }

    public async Task<ApplicationUser> ApplicationUserAsync()
    {
        var applicationUser = await this._userManager.FindByIdAsync(HttpContext.Current.User.Identity.GetUserId());
        return applicationUser;
    }

    public bool IsAuthenticated()
    {
        return HttpContext.Current.User.Identity.IsAuthenticated;
    }

    public async Task<int> CandidateIdAsync()
    {
        var applicationUser = await this.ApplicationUserAsync();
        if (applicationUser != null)
        {
            return applicationUser.CandidateId.GetValueOrDefault();
        }
        return 0;
    }
}

解决方法:

I have been searching for the simplest way to run async tasks from a non-async method.

已经对此进行了多次讨论,并且没有适用于每种情况的解决方案.内部AsyncHelper类型仅在ASP.NET小组知道安全性的特定情况下使用;这不是通用解决方案.

通常,这些方法是:

>阻止(使用Result或GetAwaiter().GetResult()).除非您始终使用ConfigureAwait(false),否则此方法为can cause deadlocks(正如我在博客中所描述的),并且您调用的所有代码也始终使用ConfigureAwait(false).但是,请注意,除非您的代码实际上不需要ASP.NET上下文,否则它不能使用ConfigureAwait(false).
>嵌套消息循环.这可以通过我的AsyncEx库中的AsyncContext之类来实现.但是,有许多ASP.NET API隐式地假定当前SynchronizationContext为AspNetSynchronizationContext,而在AsyncContext中则不是这种情况.
>具有阻塞的单独线程(使用Task.Run(…).GetAwaiter().GetResult()).这种方法避免了仅通过阻塞就可以看到的死锁,但是它确实在ASP.NET上下文之外执行代码.这种方法还会对您的可伸缩性产生负面影响(首先是在ASP.NET上使用异步的全部要点).

换句话说,这些只是黑客.

ASP.NET vNext具有“视图组件”的概念,该概念可能是异步的,因此这将是将来的自然解决方案.对于当今的代码,IMO最好的解决方案是使方法同步.这比实施黑客更好.

标签:razor,asynchronous,synchronous,c,asp-net-mvc
来源: https://codeday.me/bug/20191028/1953902.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有