使用工厂模式与ASP.NET核心依赖注入

fos*_*bie 8 c# dependency-injection .net-core asp.net-core

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

参数用于配置,来自配置文件和数据库,我不希望我的类去引用数据库和配置本身.

我认为工厂模式是最好的方法,但我无法找出使用工厂类的最佳方法,工厂类本身依赖于配置和数据库.

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

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)
    {
     ...
    }
}
Run Code Online (Sandbox Code Playgroud)

GlobalRepository构造函数如下所示:

public GlobalRepository(string mode, string apiKey)
{
}
Run Code Online (Sandbox Code Playgroud)

我现在如何将模式从配置和apiKey从DbRepository传递到Startup的构造函数中?

Nko*_*osi 12

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

//...

var mode = "get value from config";

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

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

//...
Run Code Online (Sandbox Code Playgroud)


and*_*ate 8

您可能还想检查这些链接...

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

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

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

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>>();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class DefaultFooFactory: IFooFactory{
  public IFoo create(){return new DefaultFoo();}
}
Run Code Online (Sandbox Code Playgroud)


Art*_*tur 5

ITalk我将展示通过字符串键解析实现的工厂的最小示例。该解决方案可以轻松扩展到具有任何键和实体类型的通用工厂。

为了举例,让我们定义接口ITalk和两个实现CatDog

public interface ITalk
{
    string Talk();
}

public class Cat : ITalk
{
    public string Talk() => "Meow!";
}

public class Dog : ITalk
{
    public string Talk() => "Woof!";
}
Run Code Online (Sandbox Code Playgroud)

现在定义TalkFactoryOptionsTalkFactory

public class TalkFactoryOptions
{
    public IDictionary<string, Type> Types { get; } = new Dictionary<string, Type>();

    public void Register<T>(string name) where T : ITalk
    {
        Types.Add(name, typeof(T));
    }
}

public class TalkFactory
{
    private readonly IServiceProvider _provider;
    private readonly IDictionary<string, Type> _types;

    public TalkFactory(IServiceProvider provider, IOptions<TalkFactoryOptions> options)
    {
        _provider = provider;
        _types = options.Value.Types;
    }

    public ITalk Resolve(string name)
    {
        if (_types.TryGetValue(name, out var type))
        {
            return (ITalk)_provider.GetRequiredService(type);
        }

        throw new ArgumentOutOfRangeException(nameof(name));
    }
}
Run Code Online (Sandbox Code Playgroud)

添加扩展方法以进行简单的实现注册:

public static class FactoryDiExtensions
{
    public static IServiceCollection RegisterTransientSpeaker<TImplementation>(this IServiceCollection services, string name)
        where TImplementation : class, ITalk
    {
        services.TryAddTransient<TalkFactory>();
        services.TryAddTransient<TImplementation>();
        services.Configure<TalkFactoryOptions>(options => options.Register<TImplementation>(name));
        return services;
    }
}
Run Code Online (Sandbox Code Playgroud)

并注册CatDog实现:

services
  .RegisterTransientSpeaker<Cat>("cat")
  .RegisterTransientSpeaker<Dog>("dog");
Run Code Online (Sandbox Code Playgroud)

现在您可以通过名称注入TalkFactory并解析实现:

var speaker = _factory.Resolve("cat");
var speech = speaker.Talk();
Run Code Online (Sandbox Code Playgroud)

这里的技巧是Configure<TOptions()。此方法是附加的,这意味着您可以多次调用它来配置 的同一个实例TalkFactoryOptions
正如我所说,这个示例可以转换为通用工厂,并添加注册工厂委托而不是具体类型的功能。但代码对于SO来说太长了。