如何在 ASP.NET application_start 中调用一些异步代码

How to call some async code in an ASP.NET application_start

在我们的 application_startup 中,如果不存在任何数据,我们会在数据库中植入一些假数据。

为此,我们使用 Async 方法来存储数据。伟大的。唯一的问题是,我们不确定如何在 application_startup 中执行此操作,因为那不是异步方法。

我花了太多时间试图理解@StevenCleary 的教程,但我总是遇到僵局。我完全 grok what he consistently says:

As a general rule, you should use "async all the way down"; that is, don't block on async code

但我只是不知道我该怎么做,在这种情况下:(

让我们想象这是我正在尝试使用的代码...

protected void Application_Start()
{
    var someFakeData = LoadSomeFakeData();
    var documentStore = new DocumentStore();
    await documentStore.InitializeAsync(someFakeData);

    ...

    // Registers this database as a singleton.
    Container.Register(documentStore);
}

以及后来的一些代码使用了这个 documentStore。它是通过构造注入注入的...

public SomeController(IDocumentStore documentStore)
{
    _documentStore = documentStore;
}

public ViewModel GetFoos()
{
    using (var session = _documentStore.OpenSession())
    {
        ... db code goes in here ... 
    }
}

澄清

不是试图在这里做一些异步代码。我实际上是在尝试调用这个异步方法,同步。当然,我失去了 async blah blah de blah 的好处.. 但我对此很满意。这是启动,我很乐意阻止启动。

在这种情况下,您正在异步初始化共享资源。因此,我建议您要么保存 Task 本身,要么引入异步包装类型。

使用Task:

protected void Application_Start()
{
  var someFakeData = LoadSomeFakeData();
  var documentStore = new DocumentStore();
  var documentStoreTask = documentStore.InitializeAsync(someFakeData);

  ...

  // Registers this database task as a singleton.
  Container.Register(documentStoreTask);
}

不过,这可能太尴尬了,具体取决于 Container。在那种情况下,你可以引入一个异步包装器类型:

public sealed class DocumentStoreWrapper
{
  private readonly Task<DocumentStore> _documentStore;

  public DocumentStoreWrapper(Data data)
  {
    _documentStore = CreateDocumentStoreAsync(data);
  }

  private static async Task<DocumentStore> CreateDocumentStoreAsync(Data data)
  {
    var result = new DocumentStore();
    await documentStore.InitializeAsync(data);
    ...
    return result;
  }

  public Task<DocumentStore> DocumentStoreTask { get { return _documentStore; } }
}

protected void Application_Start()
{
  var someFakeData = LoadSomeFakeData();
  var documentStoreWrapper = new DocumentStoreWrapper(someFakeData);

  ...

  // Registers this database wrapper as a singleton.
  Container.Register(documentStoreWrapper);
}

或者,您可以使用 AsyncLazy<T>,其功能大致相同,但使用后台线程来执行初始化代码。

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 MyTaskFactory
          .StartNew(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
    public static void RunSync(Func<Task> func)
    {
        MyTaskFactory
          .StartNew(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

然后用作

AsyncHelper.RunSync(ProcessAsync);

private async Task ProcessAsync(){ ....

您可以在 none 异步方法中使用 Task.Run(() => YourAsyncMethod());,例如:

    protected void Application_Start()
    {
        Task.Run(() => MyAsyncMethod(true));
    }

这是一个老话题,但它出现在我的搜索中,也许其他人也会。

对于 OP 所请求的内容(即从同步方法内部以同步方式 运行 异步方法,并阻塞直到它完成),是否有某种原因使用 Task.WaitAll 不是解决这个问题的简单而充分的方法吗?

protected void Application_Start()
{
    Task.WaitAll(MyAsyncMethod(true));
}