Autofac 中基于环境的不同 DI 实现

Different DI implementation in Autofac based on Environment

我正在尝试根据环境为我的界面 IBlobService 设置不同的 DI 实现。 对于 Dev,我想使用 LocalFileStorageRepository,而对于 Production,我想使用 AzureStorageRepository 以及需要 BlobServiceClient.

的构造函数
builder.Register(c =>
{
    var environment = Environment.GetEnvironmentVariable("environment_name");
    if (environment == "DEV")
    {
        return new LocalFileStorageRepository();
    }
    else
    {
        return new AzureStorageRepository(c.Resolve<BlobServiceClient>());
    }
}).As<IBlobService>().InstancePerLifetimeScope();

它不起作用,因为 The type arguments for method 'RegistrationExtensions.Register<T>(ContainerBuilder, Func<IComponentContext, T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

知道如何解决这个问题吗?

尝试明确指定类型参数。

Try specifying the type arguments explicitly.

或者考虑让委托 return 成为一个普通类型

例如

//...

builder.RegisterType<LocalFileStorageRepository>();
builder.RegisterType<AzureStorageRepository>();
builder.RegisterType<BlobServiceClient>(); //<-- May need further details
builder.Register(c => {
    var environment = Environment.GetEnvironmentVariable("environment_name");
    return (IBlobService) (environment == "DEV" 
        ? c.Resolve<LocalFileStorageRepository>()
        : c.Resolve<AzureStorageRepository>());
    
}).InstancePerLifetimeScope();

//...

注意 return 语句中的转换。

BlobServiceClient 的注册可能需要进一步细化,具体取决于 AzureStorageRepository

的显式依赖关系

除了@Nkosi answer - if you are able to use standard ASPNETCORE_ENVIRONMENT variable for specifying/determining environment there is one other option: use multiple Startup classes with name conventions described in the docs

The app can define multiple Startup classes for different environments. The appropriate Startup class is selected at runtime. The class whose name suffix matches the current environment is prioritized. If a matching Startup{EnvironmentName} class isn't found, the Startup class is used.

所以在你的情况下你可以定义“base”Startup(并将其用作默认值)并将它与 template method pattern 组合起来看起来像这样(示例代码,甚至可能无法编译, 只是给你一个想法):

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        ...
        RegisterEnvironmentAutofacServices(builder);
        ...
    }
    
    protected virtual RegisterEnvironmentAutofacServices(ContainerBuilder builder)
    {
        // do default registrations here or throw depended on your preferences
        builder.Register(c => new AzureStorageRepository(c.Resolve<BlobServiceClient>()))
            .As<IBlobService>()
            .InstancePerLifetimeScope();
    }
}

public class StartupDEV : Startup
{
    public Startup(IConfiguration configuration): base(configuration)
    {
    }

    protected override RegisterEnvironmentAutofacServices(ContainerBuilder builder)
    {
        // do environment specific ones:

        builder.Register(c => new LocalFileStorageRepository())
            .As<IBlobService>()
            .InstancePerLifetimeScope();
    }
}

对于在运行时不会更改的配置值,而是 wrap Registerif-else 检查中调用。这个:

  • 简化注册
  • 提高性能
  • 确保在启动时检查环境变量的可用性

换句话说,将您的注册重写为以下内容:

var environment = Environment.GetEnvironmentVariable("environment_name");
if (environment == "DEV")
{
    builder.RegisterType<LocalFileStorageRepository>().As<IBlobService>()
        .InstancePerLifetimeScope();
}
else
{
    builder.RegisterType<AzureStorageRepository>().As<IBlobService>()
        .InstancePerLifetimeScope();
}

或可选

var environment = Environment.GetEnvironmentVariable("environment_name");
var blogServiceType = environment == "DEV"
    ? typeof(LocalFileStorageRepository)
    : typeof(AzureStorageRepository);

builder.RegisterType(blogServiceType).As<IBlobService>().InstancePerLifetimeScope();