如何在Startup.Configure中处理异步操作?

Dev*_*awk 64 c# async-await asp.net-core

在我的ASP.NET 5应用程序中,我想将Azure中的一些数据加载到Startup.Configure方法中的缓存中.Azure SDK专门公开异步方法.通常,调用异步方法是通过等待异步方法来完成的,如下所示:

public async Task Configure(IApplicationBuilder app, IMemoryCache cache)
{
    Data dataToCache = await DataSource.LoadDataAsync();
    cache.Set("somekey", dataToCache);

    // remainder of Configure method omitted for clarity
}
Run Code Online (Sandbox Code Playgroud)

但是,ASP.NET 5要求Configure方法返回void.我可以使用异步void方法,但我的理解是异步void方法只应该用于事件处理程序(根据https://msdn.microsoft.com/en-us/magazine/jj991977.aspx等等) ).

我认为更好的方法是在没有等待的情况下调用异步函数,在返回的Task上调用Wait,然后通过Task.Results属性缓存结果,如下所示:

public void Configure(IApplicationBuilder app, IMemoryCache cache)
{
    Task<Data> loadDataTask = DataSource.LoadDataAsync();
    loadDataTask.Wait();
    cache.Set("somekey", loadDataTask.Result);

    // remainder of Configure method omitted for clarity
}
Run Code Online (Sandbox Code Playgroud)

Stephen Walther在今年早些时候的博客文章中采用了类似的方法.但是,如果这被认为是可以接受的做法,则从该帖子中不清楚.是吗?

如果这被认为是可接受的做法,我需要什么 - 如果有的话 - 错误处理?我的理解是Task.Wait()将重新抛出异步操作引发的任何异常,并且我没有提供任何取消异步操作的机制.简单地调用Task.Wait()就足够了吗?

Ste*_*ary 33

您链接到的博客中的示例代码仅使用sync-over-async来使用示例数据填充数据库; 该调用在生产应用程序中不存在.

首先,我要说如果你真的需要Configure异步,那么你应该向ASP.NET团队提出一个问题,这样就可以了.他们ConfigureAsync在此时(即发布之前)添加对a的支持并不太难.

其次,你有几种解决问题的方法.您可以使用task.Wait(或者更好的是,如果确实发生错误,则会task.GetAwaiter().GetResult()避免使用AggregateException包装器).或者,您可以缓存任务而不是任务结果(如果IMemoryCache更多的是字典而不是一些奇怪的序列化到二进制数组内存中的东西 - 我正在看着你,以前版本的ASP .净).

如果这被认为是可接受的做法,我需要什么 - 如果有的话 - 错误处理?

使用GetAwaiter().GetResult()会导致异常(如果有)传播出来Configure.但是,如果配置应用程序失败,我不确定ASP.NET会如何响应.

我没有提供任何取消异步操作的机制.

我不确定你如何"取消" 应用程序的设置,所以我不担心它的那一部分.

  • 关于你的第一点,过去几年ASP.NET团队提出了各种各样的问题.目前的化身似乎是[问题#1088](https://github.com/aspnet/Hosting/issues/1088)并且它不会比v3.0更早被接收,而v3.0不在[路线图](https://github.com/aspnet/Home/wiki/Roadmap),意思是2018年的某个时间. (7认同)

jos*_*ode 12

Dotnet Core 3.x 对此提供了更好的支持。

首先,您可以为缓存过程创建一个类。让它实现IHostedService如下。只需实现两个功能:

    private readonly IServiceProvider _serviceProvider;
    public SetupCacheService(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        // Perform your caching logic here. 
        // In the below example I omit the caching details for clarity and 
        // instead show how to get a service using the service provider scope. 
        using (var scope = _serviceProvider.CreateScope())
        {
            // Example of getting a service you registered in the startup
            var sampleService = scope.ServiceProvider.GetRequiredService<IYourService>();

            // Perform the caching or database or whatever async work you need to do. 
            var results = sampleService.DoStuff();
            var cacheEntryOptions = new MemoryCacheEntryOptions(){ // cache options };

            // finish caching setup..
        }
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
Run Code Online (Sandbox Code Playgroud)

现在,在 Starup.cs

    public virtual void ConfigureServices(IServiceCollection services)
    {
        // Normal service registration stuff. 
        // this is just an example. There are 1000x ways to do this. 
        services.AddTransient(IYourService, ConcreteService);

       // Here you register the async work from above, which will 
       // then be executed before the app starts running
       services.AddHostedService<SetupCacheService>();
    }
Run Code Online (Sandbox Code Playgroud)

就是这样。请注意,我对此的解决方案在很大程度上依赖于Andrew Lock 的文章。我非常感谢他花时间写下来。

来自我发布的 Andrew Lock 的链接,

服务将在启动时按照它们添加到 DI 容器的相同顺序执行,即稍后在 ConfigureServices 中添加的服务将在启动时执行。

希望这可以帮助任何寻找 Dotnet 核心 3.x+ 方法的人。


mbu*_*nik 1

您可以执行一些异步工作,但该方法是同步的,您无法更改它。这意味着您需要同步等待异步调用完成。

如果启动尚未完成,您不想从 Startup 方法返回,对吗?你的解决方案似乎没问题。

至于异常处理:如果有一项工作没有您的应用程序就无法正常运行,您应该让 Startup 方法失败(请参阅Fail-fast)。如果这不是关键问题,我会将相关部分包含在 try catch 块中,然后记录问题以供以后检查。


归档时间:

查看次数:

9471 次

最近记录:

7 年,3 月 前