【问题标题】:Make Async Calls inside a Controller Action在控制器操作中进行异步调用
【发布时间】:2015-02-06 16:06:00
【问题描述】:

我有一个控制器操作,它获取文档类型列表,然后为每个文档类型进行 Web 服务调用。我想一次完成所有这些,这样循环它们只需要最长的时间。我不知道我的代码是否正确,我需要做其他事情,或者我的代码是否完全不正确。

行动:

public ActionResult GetPlan(MemberViewModel request)
{
    DocService ds = new DocService();

    List<DocType> docTypes = ds.GetDocTypesForPlan(request.PlanId);

    List<CoverageDocument> coverageDocuments = ds.GetDocumentsForDocTypes(docTypes);

    return View(coverageDocuments);
}

GetDocumentsForDocTypes:

public List<CoverageDocument> GetDocumentsForDocTypes(List<DocType> planDocTypes)
{
    List<CoverageDocument> planDocuments = new List<CoverageDocument>();

    DocumentUtility documentUtility = new DocumentUtility();
    int lastYear = DateTime.Now.Year - 1;

    planDocTypes.ForEach(async (docType) =>
    {
        DocumentUtility.SearchCriteria sc = new DocumentUtility.SearchCriteria();
        sc.documentType = docType;
        Dictionary<long, Tuple<string, string>> documentList = await documentUtility.FindDocuments(sc);

        documentList.ToList().ForEach((document) =>
            {
                CoverageDocument doc = this.coverageDocumentConstructor(document);
                planDocuments.Add(doc);
            });
    });

    return planDocuments;
}

例外:

附加信息:异步操作无法在 这次。异步操作只能在一个 异步处理程序或模块或在页面中的某些事件期间 生命周期。如果在执行页面时发生此异常,请确保 该页面被标记为 。这个例外可能 还表示尝试调用“async void”方法,即 在 ASP.NET 请求处理中通常不受支持。相反, 异步方法应该返回一个Task,调用者应该等待 它。

【问题讨论】:

    标签: c# .net asp.net-mvc task-parallel-library async-await


    【解决方案1】:

    您的代码不正确。通过将异步 lambda 发送到 ForEach 扩展方法,您将强制它为 async void,这在 UI 事件处理程序之外绝不是一个好主意。

    要真正异步,您的调用需要一直异步:

    public async Task<ActionResult> GetPlan(MemberViewModel request)
    {
        DocService ds = new DocService();
    
        List<DocType> docTypes = ds.GetDocTypesForPlan(request.PlanId);
    
        List<CoverageDocument> coverageDocuments = await ds.GetDocumentsForDocTypesAsync(docTypes);
    
        return View(coverageDocuments);
    }
    
    public async Task<List<CoverageDocument>> GetDocumentsForDocTypesAsync(List<DocType> planDocTypes)
    {
        DocumentUtility documentUtility = new DocumentUtility();
        int lastYear = DateTime.Now.Year - 1;
    
        var planDocuments = await Task.WhenAll(planDocTypes.Select(async (docType) =>
        {
            DocumentUtility.SearchCriteria sc = new DocumentUtility.SearchCriteria();
            sc.documentType = docType;
    
            return await documentUtility.FindDocuments(sc).Select((document) => this.coverageDocumentConstructor(document))
        }));
    
        return planDocuments.SelectMany(doc => doc);;
    }
    

    【讨论】:

    • 好的,这就是我读到的异步 void。看来我不应该在堆栈中一直保持异步。所以我现在必须更改我的界面签名? Parallel.ForEach 是我想要的(不同的吗?)?
    • I3arnon,为清楚起见,假设 FindDocument 支持异步,您需要将代码从 documentUtility.FindDocuments(sc) 更改为 documentUtility.FindDocumentsAsync(sc)。
    • @Son_of_Sam 这不是我的代码,而是 OP 的。但这是真的,就像我在其他方法中所做的那样
    • @MStodd 这取决于您要实现的目标。如果您只想使用多个线程来并行化 CPU 密集型工作,那么可以。如果FindDocuments 真的是async,那将是对线程的浪费。
    • @I3arnon 不是 CPU 密集型,相对较长(5 秒)的 Web 服务调用。 FindDocuments 是异步的(我拥有它),它会进行异步服务调用。我可以改为进行同步调用,并使用 Parallel.Foreach
    猜你喜欢
    • 2013-12-04
    • 1970-01-01
    • 2015-08-20
    • 2015-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-25
    • 1970-01-01
    相关资源
    最近更新 更多