繁体   English   中英

简单的 Injector 构造函数参数

[英]Simple Injector constructor parameter

我在项目中使用 Simple Injector 作为 DI 容器。

问题是我有一个SqliteStorage -class,它需要数据库的路径。 有多个数据库,所以我需要一种方法来在创建时注入SqliteStorage -class 的路径。

我的代码如下所示(简化后没有接口):

public class SqliteStorageOptions
{
    public string Path {get; set;}
}

public class SqliteStorage
{
    private readonly string _path;

    public SqliteStorage(SqliteStorageOptions options)
    {
        _path = options.Path;
    }
}

public class Db1
{
    private readonly SqliteStorage _sqlite;

    public Db1(SqliteStorage sqlite)
    {
        _sqlite = sqlite;
    }
}

public class Db2
{
    private readonly SqliteStorage _sqlite;

    public Db1(SqliteStorage sqlite)
    {
        _sqlite = sqlite;
    }
}


// without di
var db1 = new Db1(new SqliteStorage(new SqliteStorageOptions { Path = "db1.db" });
var db2 = new Db2(new SqliteStorage(new SqliteStorageOptions { Path = "db2.db" });

可能的解决方案:

  1. SqliteStorage的每个方法中包含SqliteStorageOptions作为参数。
  2. SqliteStorage中提供一个init方法
  3. 使用public SqliteStorage Create(SqliteStorageOptions options)方法创建一个SqliteStorageFactory

那么在简单注射器中是否有任何内置解决方案可以解决我的问题,或者有人可以提供另一种(更好的)解决方案吗?

谢谢

编辑1:我添加了一些代码。 Db1Db2都连接到 sqlite-dbs(不同的数据库,不同的模式),所以我想将所有 sqlite-stuff 提取到它自己的 class SqliteStorage 因此, SqliteStorage需要知道数据库路径。

哪种解决方案最好取决于您是否需要自动接线(自动构造函数注入)。 使用条件注册(使用RegisterConditional )是一个不错的选择,但您知道它仅限于仅根据其直接父项来确定注入。 这意味着您不能使SqliteStorageOptions基于其父父级( Db1Db2 )有条件。

如果Db1Db2类仅依赖于SqliteStorage并且不需要任何其他依赖项,则自动装配不是真正的问题,您的注册可以像以下一样简单:

container.Register<Db1>(
    () => new Db1(new SqliteStorage(new SqliteStorageOptions { Path = "db1.db" }));
container.Register<Db2>(
    () => new Db2(new SqliteStorage(new SqliteStorageOptions { Path = "db2.db" });

如果在Db1Db2中需要自动连线, RegisterConditional提供了一个很好的选择,因为它启用了自动连线:

container.Register<Db1>();
container.Register<Db2>();

container.RegisterConditional<SqliteStorage>(
    Lifestyle.CreateRegistration(
        () => new SqliteStorage(new SqliteStorageOptions { Path = "db1.db" }),
        container),
    c => c.Consumer.ImplementationType == typeof(Db1));

container.RegisterConditional<SqliteStorage>(
    Lifestyle.CreateRegistration(
        () => new SqliteStorage(new SqliteStorageOptions { Path = "db2.db" }),
        container),
    c => c.Consumer.ImplementationType == typeof(Db2)); 

在此代码片段中, Db1Db2都“正常”注册,而SqliteStorage注册是根据消费者有条件地注入的。

这个注册更加复杂,因为RegisterConditonal需要提供一个Registration实例:没有直接接受Func<T>工厂委托的RegisterConditional重载。

每个数据库连接可以有 2 个单例。 让我们考虑一个示例,首先我们需要为您的 StorageService 创建一个接口:

public interface IStorage
{
    void UsePath();
}

现在让我们创建这个存储服务的几个实现:

public class RedisStorage: IStorage
{
    private readonly string _path;

    public RedisStorage(string path)
    {
        _path = path;
    }

    public void UsePath()
    {
        Console.WriteLine($"Here's path: {_path}");
    }
}

public class SqlStorage: IStorage
{
    private readonly string _path;

    public SqlStorage(string path)
    {
        _path = path;
    }

    public void UsePath()
    {
        Console.WriteLine($"Here's path: {_path}");
    }
}

枚举以区分 IStorage 的实现:

public class StorageSource
{
    public enum StorageTypes
    {
        Redis=1,
        Sql=2
    }
}

完成后,让我们为存储源创建一个包装器:

public interface IStorageWrapper
{
    void DoStuff();
}

现在来了一个棘手的部分,实例化一个存储包装服务装饰器:

public class StorageServiceWrapper: IStorageWrapper
{
    private readonly Func<string, IStorage> _storage;

    public StorageServiceWrapper(Func<string, IStorage> storage)
    {
        _storage = storage;
    }

    public void UsePath()
    {
        _storage(StorageSource.StorageTypes.Redis.ToString()).DoStuff();
        //uncomment for sql
        //_storage(StorageSource.StorageTypes.Sql.ToString()).DoStuff();
    }
}

为此,您需要在 Startup.cs 中注册您的类,如下所示:

services.AddScoped<IStorageWrapper, StorageServiceWrapper>();  
  
services.AddSingleton<RedisStorage>();  
services.AddSingleton<SqlStorage>();  
  
services.AddTransient<Func<string, IStorage>>(serviceProvider => key =>  
{  
    switch (key)  
    {  
        case "Redis":  
            return serviceProvider.GetService<RedisStorage>();  
        default:  
            return serviceProvider.GetService<SqlStorage>();  
    }  
}); 

这不如调用_storage.DoStuff(); ,但我相信会帮助您解决问题。 如果您仍然想保持方便,请考虑管理您的设置文件并使用您需要的 conn 字符串注入适当的 IOptions<> 实例并注册工厂方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM