首页 > 解决方案 > 我们可以从构造函数中调用异步方法吗?

问题描述

我需要从 mvc 应用程序调用第三方异步方法。此异步方法的名称是 ForceClient.QueryAsync。它来自一个开源项目:https ://github.com/developerforce/Force.com-Toolkit-for-NET/ 。

下面工作正常,当进程处于 mvc 的 View 阶段时,model.Opportunity 包含预期的信息:

public async Task<ActionResult> MyController(string Id) {
    . . . .
    MyModel model = new MyModel();
    var client = new ForceClient(instanceUrl, accessToken, apiVersion);
    var qry = await client.QueryAsync<MyModel.SFOpportunity>(
            "SELECT Name, StageName FROM Opportunity where  Id='" + Id + "'");
    model.Opportunity = qry.Records.FirstOrDefault();
    . . . .
return View(viewName, myModel);
}

但下面不起作用。当流程处于 View 阶段时,model.Opportunity 为 null。我做了一些调试,发现流程是这样的:

1) 第一步

2) 第二步

3) 在视图阶段。此时 model.Opportunity 为空,我需要填充它。

4) 第三步。

public async Task<ActionResult> MyController(string Id) {
    . . . .
    MyModel myModel = await Task.Run(() =>
        {
            var result = new MyModel(Id);
            return result;

        });   // =====> Step 1
    . . . .
return View(viewName, myInfoView);
}    

public class MyModel
{
    public SFOpportunity Opportunity { get; set; }
    public MyModel(string id)
    {
        setOpportunityAsync(id);
    }

    private async void setOpportunityAsync(string id)
    {
        . . .
        var client = new ForceClient(instanceUrl, accessToken, apiVersion);
        var qry = await client.QueryAsync<MyModel.SFOpportunity>(
             "SELECT Name, StageName FROM Opportunity where  Id='" + id + "'");  // ======>  Step2 
        Opportunity = qry.Records.FirstOrDefault();  // =====> step3
    }

所以,我的问题是我需要做什么才能让它按以下顺序执行步骤:1)Step1

2) 第二步

3) 第三步

4) 在视图阶段。此时应该填充 model.Opportunity。

标签: c#

解决方案


for 的构造函数MyModel不会(也不能)等待setOpportunityAsync,因为构造函数本身不是(也不能是)异步的。否则,您将能够等待对构造函数本身的调用,但您不能。因此,在调用构造函数后,异步方法可能不会立即完成执行。它将完成……无论何时完成。

这是一个较小的测试类来说明行为:

public class HasConstructorWithAsyncCall
{
    public HasConstructorWithAsyncCall()
    {
        MarkConstructorFinishedAsync();
    }

    public bool ConstructorHasFinished { get; private set; }

    async void MarkConstructorFinishedAsync()
    {
        await Task.Delay(500);
        ConstructorHasFinished = true;
    }
}

ConstructorHasFinished实例构建后立即的值是多少?这是一个单元测试:

[TestMethod]
public void TestWhenConstructorFinishes()
{
    var subject = new HasConstructorWithAsyncCall();
    Assert.IsFalse(subject.ConstructorHasFinished);
    Thread.Sleep(600);
    Assert.IsTrue(subject.ConstructorHasFinished);
}

测试通过。构造函数在MarkConstructorFinishedAsync尚未完成时返回,因此ConstructorHasFinished为 false。半秒后它完成,值为真。

你不能标记一个构造函数async,所以你不能await在构造函数中做任何事情。

一般来说,我们不会将任何像数据检索这样的长时间运行的东西放在构造函数中,包括我们会异步调用的任何东西。如果我们这样做,那么我们必须要么同步调用它,要么知道构造函数的完成并不意味着它是完全“构造的”。


推荐阅读