I'm using Simple Injector as DI Container in a project.
The problem is that I have a SqliteStorage
-class, which needs the path to the db. There are multiple dbs, so I need a way to inject the path to the SqliteStorage
-class at creation.
My code looks as follows (simplified without interfaces):
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" });
Possible Solutions:
SqliteStorageOptions
as parameter at every method in SqliteStorage
.init
-method in SqliteStorage
SqliteStorageFactory
with a public SqliteStorage Create(SqliteStorageOptions options)
-method.So are there any built-in solution to my problem in simple-injector or can someone provide another (better) solution?
Thanks
Edit 1:
I added some code. Db1
and Db2
both connect to sqlite-dbs (different dbs, different schema), so I wanted to extract all the sqlite-stuff to its own class SqliteStorage
. So, the SqliteStorage
needs to know the db path.
Which solution is best depends a bit on whether you require Auto-Wiring (automatic constructor injection) or not. Using conditional registrations (using RegisterConditional
) is a good pick, but you have be aware that it is limited to determining the injection based on only its direct parent. This means that you can't make SqliteStorageOptions
conditional based on its parent parent (either Db1
or Db2
).
If the Db1
and Db2
classes solely depend on a SqliteStorage
and don't require any other dependencies, Auto-Wiring is not a real issue and your registrations can be as simple as the following:
container.Register<Db1>( () => new Db1(new SqliteStorage(new SqliteStorageOptions { Path = "db1.db" })); container.Register<Db2>( () => new Db2(new SqliteStorage(new SqliteStorageOptions { Path = "db2.db" });
In case Auto-Wiring is required inside Db1
and Db2
, RegisterConditional
gives a good alternative, because it enables Auto-Wiring:
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));
In this code snippet, both Db1
and Db2
are registered 'normally', while the SqliteStorage
registrations are conditionally injected based on thei consumer.
This registration is more complex, because RegisterConditonal
need to be supplied with a Registration
instance: there is no RegisterConditional
overload that directly accepts a Func<T>
factory delegate.