![](/img/trans.png)
[英]Parameter not registered when using Simple Injector to inject into an MVC Controller Constructor
[英]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" });
可能的解决方案:
SqliteStorage
的每个方法中包含SqliteStorageOptions
作为参数。SqliteStorage
中提供一个init
方法public SqliteStorage Create(SqliteStorageOptions options)
方法创建一个SqliteStorageFactory
。那么在简单注射器中是否有任何内置解决方案可以解决我的问题,或者有人可以提供另一种(更好的)解决方案吗?
谢谢
编辑1:我添加了一些代码。 Db1
和Db2
都连接到 sqlite-dbs(不同的数据库,不同的模式),所以我想将所有 sqlite-stuff 提取到它自己的 class SqliteStorage
。 因此, SqliteStorage
需要知道数据库路径。
哪种解决方案最好取决于您是否需要自动接线(自动构造函数注入)。 使用条件注册(使用RegisterConditional
)是一个不错的选择,但您知道它仅限于仅根据其直接父项来确定注入。 这意味着您不能使SqliteStorageOptions
基于其父父级( Db1
或Db2
)有条件。
如果Db1
和Db2
类仅依赖于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" });
如果在Db1
和Db2
中需要自动连线, 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));
在此代码片段中, Db1
和Db2
都“正常”注册,而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.