C# 瞬态ASP.NET内核的异步初始化

C# 瞬态ASP.NET内核的异步初始化,c#,asp.net-core,dependency-injection,async-await,C#,Asp.net Core,Dependency Injection,Async Await,我有一个临时服务,它必须进行异步初始化(连接到不同的服务器)。 它公开了一个属性public Task InitTask,用户可以等待该属性直到服务初始化。 它还公开了在InitTask完成后可以保存访问的子服务 public class Service { public Task InitTask { get; } public ISubService1 SubService1 { get; } public ISubService2 SubService2 { ge

我有一个临时服务,它必须进行异步初始化(连接到不同的服务器)。 它公开了一个属性
public Task InitTask
,用户可以等待该属性直到服务初始化。 它还公开了在
InitTask
完成后可以保存访问的子服务

public class Service
{
    public Task InitTask { get; }

    public ISubService1 SubService1 { get; }
    public ISubService2 SubService2 { get; }
    public ISubService3 SubService3 { get; }
}
连接到其他服务器所提供的所有功能都由这些子服务控制。 通常我会注入主服务,然后等待它完成初始化,然后使用其中一个子服务。 但我只想注入这些子服务

起初我试过了

services.AddTransient<Service>()
    .AddTransient(provider =>
    {
        var server = provider.GetService<Service>();
        return server.SubService1;
    })
    .AddTransient(provider =>
    {
        var server = provider.GetService<Service>();
        return server.SubService2;
    })
    .AddTransient(provider =>
    {
        var server = provider.GetService<Service>();
        return server.SubService3;
    });
在启动时做什么

services.AddTransient<Service>()
    .AddTransient<ISubService1, WrapperSubService1>()
    .AddTransient<ISubService2, WrapperSubService2>()
    .AddTransient<ISubService3, WrapperSubService3>();
services.AddTransient()
.AddTransient()
.AddTransient()
.AddTransient();
但这也有一个明显的缺陷:代码重复

我希望的是:

services.AddTransient<Service>()
    .AddTransient(async provider =>
    {
        var server = provider.GetService<Service>();
        await server.InitTask;
        return server.SubService1;
    })
    .AddTransient(async provider =>
    {
        var server = provider.GetService<Service>();
        await server.InitTask;
        return server.SubService2;
    })
    .AddTransient(async provider =>
    {
        var server = provider.GetService<Service>();
        await server.InitTask;
        return server.SubService3;
    });
services.AddTransient()
.AddTransient(异步提供程序=>
{
var server=provider.GetService();
等待server.InitTask;
返回server.SubService1;
})
.AddTransient(异步提供程序=>
{
var server=provider.GetService();
等待server.InitTask;
返回server.SubService2;
})
.AddTransient(异步提供程序=>
{
var server=provider.GetService();
等待server.InitTask;
返回server.SubService3;
});
但这只会公开
任务
以供注入


有什么想法吗?

我认为在这种情况下,您实际上可以使用显式接口实现:

服务类更改为

公共类服务:ISubService1、ISubService2、ISubService3
{
公共任务初始化任务{get;}
公共ISubService1子服务1{get;}
公共ISubService2子服务2{get;}
公共ISubService3子服务3{get;}
....
专用异步值任务GetSubService1Async()
{
等待server.InitTask
返回server.SubService1;
}
//接口实现
异步任务ISubService1.GetExampleAncy(…)
{
var subService1=等待这个。GetSubService1Async();
return wait subService1.getExampleAscync(…);
}
....
异步任务ISubService2.getExampleAync(…)
{
var subService1=wait this.GetSubService2Async();
return wait subService1.getExampleAscync(…);
}
//等等
}

对于这种情况,我找到的唯一解决方案是:

  • 使初始化同步。特别是在ASP.NET上,我认为这是一个很好的选择,因为在初始化完成之前不接收请求是有意义的
  • 在部署服务之前,有一个单独的“schema deployer”项目来进行这种初始化(创建队列/表)。这是我倾向于在现代应用程序中使用的解决方案

  • 我确实认为公开
    任务
    很好

    至于解析服务的代码,它看起来像:

    var subService1 = await serviceProvider.GetService<Task<SubService1>>();
    var result = await subService1.GetExampleAsync();
    
    var subService1=await serviceProvider.GetService();
    var result=await subService1.getExampleAync();
    
    它看起来很优雅。或者您可以编写一个扩展方法,如:

    public static Task<T> GetServiceAsync<T>(this IServiceProvider provider) => provider.GetService<Task<T>>();
    
    公共静态任务GetServiceAsync(此IServiceProvider提供程序)=>provider.GetService();
    
    然后

    var subService1=等待serviceProvider.GetServiceAsync();
    
    会更清楚

    此外,基于
    Microsoft.Extensions.DependencyInjection
    的默认DI的设计不像对象容器,而只是一个IoC接口(这与众所周知的DI容器(如autofac)不同)。我们不应该直接从提供者检索每个对象,即使它看起来更优雅


    因此,直接从主服务获取子服务
    subService1=provider.GetService().GetService1Async()将是推荐的方式。

    相关:相关:相关:为什么子服务定义为
    服务的属性?如果它们可以单独使用,但依赖于
    服务
    ,那么为什么不将
    服务
    注入到您的子服务中呢?
    public static Task<T> GetServiceAsync<T>(this IServiceProvider provider) => provider.GetService<Task<T>>();
    
    var subService1 = await serviceProvider.GetServiceAsync<SubService1>();