将工厂模式与ASP.NET Core依赖注入配合使用

时间:2019-01-10 11:10:26

标签: c# asp.net-core .net-core

我需要ASP.Net Core依赖项注入才能将一些参数传递给实现ICardPaymentRepository接口的GlobalRepository类的构造函数。

这些参数是用于配置的,来自配置文件和数据库,我不希望我的班级去引用数据库和配置本身。

我认为工厂模式是执行此操作的最佳方法,但我无法找出使用工厂类的最佳方法,该工厂类本身依赖于配置和数据库。

我的创业公司目前看起来像这样:

public class Startup
{
    public IConfiguration _configuration { get; }
    public IHostingEnvironment _environment { get; }

    public Startup(IConfiguration configuration, IHostingEnvironment environment)
    {
        _configuration = configuration;
        _environment = environment;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IDbRepository, DbRepository>();
        var connection = _configuration.GetConnectionString("DbConnection");
        services.Configure<ConnectionStrings>(_configuration.GetSection("ConnectionStrings"));
        services.AddDbContext<DbContext>(options => options.UseSqlServer(connection));
        services.AddScoped<ICardPaymentRepository, GlobalRepository>();
        ...
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IRFDbRepository rFDbRepository)
    {
     ...
    }
}

GlobalRepository构造函数如下:

public GlobalRepository(string mode, string apiKey)
{
}

现在如何从启动模式将配置模式和DbRepository的apiKey传递给启动程序?

4 个答案:

答案 0 :(得分:7)

注册存储库时使用工厂委托重载

//...

var mode = "get value from config";

services.AddScoped<ICardPaymentRepository, GlobalRepository>(sp => {        
    var repo = sp.GetService<IDbRepository>();
    var apiKey = repo.GetApiKeyMethodHere();

    return new GlobalRepository(mode, apiKey);
});

//...

答案 1 :(得分:1)

您可能还需要检查这些链接...

https://github.com/Microsoft/AspNetCoreInjection.TypedFactories

https://espressocoder.com/2018/10/08/injecting-a-factory-service-in-asp-net-core/

关于最后一个链接,代码基本上是:

HttpContext.Request.Form.Files;

我认为温莎城堡的类型化工厂会处理掉它们在处置时创建的所有内容(这可能并非总是最好的主意),如果您仍然期望这种行为,则可能需要考虑这些链接。当我重新考虑为什么要工厂时,我最终只是创建了一个包装了新工厂的简单工厂,例如:

public class Factory<T> : IFactory<T>
{
    private readonly Func<T> _initFunc;

    public Factory(Func<T> initFunc)
    {
        _initFunc = initFunc;
    }

    public T Create()
    {
        return _initFunc();
    }
}

public static class ServiceCollectionExtensions
{
    public static void AddFactory<TService, TImplementation>(this IServiceCollection services) 
    where TService : class
    where TImplementation : class, TService
    {
        services.AddTransient<TService, TImplementation>();
        services.AddSingleton<Func<TService>>(x => () => x.GetService<TService>());
        services.AddSingleton<IFactory<TService>, Factory<TService>>();
    }
}

答案 2 :(得分:1)

我遇到了同样的问题,并通过为 IFactory<TService>IFactory<T, TService>IFactory<T1, T2, TService> 等注册一组开放泛型解决了这个问题。启动时调用一次即可添加然后,此设施允许注入/解析任何 IFactory<...>,这将为给定的参数类型集实例化 TService 的实例,前提是存在最后一个参数与 T* 类型匹配的构造函数工厂通用。下面的源代码、NuGet 包和解释性博客文章:

https://github.com/jmg48/useful

https://www.nuget.org/packages/Ariadne.Extensions.ServiceCollection/

https://jon-glass.medium.com/abstract-factory-support-for-microsoft-net-dependency-injection-3c3834894c19

答案 3 :(得分:0)

其他答案的替代方案。跟随options pattern

首先为你的配置引入一个强类型;

public class RespositoryOptions {
    public string Mode { get; set; }
    public string ApiKey { get; set; }
}

public GlobalRepository(IOptions<RespositoryOptions> options) {
    // use options.Value;
}

如果您愿意,您仍然可以使用服务工厂方法来解包 IOptions<RespositoryOptions>。但是随后您就无法验证您的服务依赖项是否已全部满足。

然后你可以从配置中播种你的选项;

public void ConfigureServices(IServiceCollection services) {
    ...
    services.Configure<RespositoryOptions>(_configuration.GetSection(name));
    ...
}

并编写另一个服务以从其他服务(如数据库)更新该选项实例;

public class ConfigureRespositoryOptions : IConfigureOptions<RespositoryOptions> {
    private readonly IDbRepository repo;
    public ConfigureRespositoryOptions(IDbRepository repo) {
        this.repo = repo;
    }
    public void Configure(RespositoryOptions config) {
        string apiKey = repo.GetApiKeyMethodHere();
    }
}