我们可以从构造函数中调用异步方法吗?

时间:2019-05-20 16:57:04

标签: c#

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

下面的模型正常工作。当进程处于mvc的View阶段时,机会包含预期的信息:

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)第1步

2)第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)第2步

3)第三步

4)在查看阶段。此时,应该填充模型。

2 个答案:

答案 0 :(得分:1)

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。半秒钟后它结束,并且值为true。

您无法标记构造函数async,因此无法await构造函数中的任何内容。

通常,我们不会将任何长期运行的东西(例如数据检索)放入构造函数中,包括我们会异步调用的任何东西。如果这样做,则必须同步调用它,或者知道构造函数的完成并不意味着它是完全“构造的”。

答案 1 :(得分:1)

您不能拥有async构造函数。

One alternative is to have async factory methods

public class MyModel
{
  public SFOpportunity Opportunity { get; set; }
  private MyModel() { }

  public static async Task<MyModel> CreateAsync(string id)
  {
    var result = new MyModel();
    await result.setOpportunityAsync(id);
    return result;
  }

  private async Task setOpportunityAsync(string id)
  {
    ...
  }
}