I have classes: X1 <- Y <- Z <- Config
(arrow means injection via constructor) and X2 <- Y <- Z <- Config
. Z
expects some configuration (Config
class), but the instance depends on the final type: X1
and X2
(type itself or a key they have defned somehow). In this example there should be two different instances of each Y
, Z
and Config
class.
How can I use different Config
in Z
depending on where it finally is used (X1
or X2
)?
class X1
{
public X1(Y y)
{
int c = y.Z.Config.C; // This config variable is connected with X1.
}
}
class X2
{
public X2(Y y)
{
int c = y.Z.Config.C; // This config variable is different than the one for X1.
}
}
class Y
{
public Z Z { get; }
public Y(Z z)
{
Z = z;
}
}
class Z
{
public Config Config { get; }
public Z(Config config)
{
Config = config;
}
}
class Config
{
public int C { get; set; }
}
I could do sth like below, but it seems very fishy and smelly (a rough example):
Bind<Config>().ToMethod(x =>
{
// Return proper config object depending on the classes found in the injection chain...
IRequest req = x.Request;
while (req != null)
{
if (req.Service.UnderlyingSystemType == typeof(X1))
{
return configForX1;
}
if (req.Service.UnderlyingSystemType == typeof(X2))
{
return configForX2;
}
req = req.ParentRequest;
}
throw new Exception("Oh no.");
});
Or to make it less fishy should I do:
class X1
{
public X1([Named("X1")] Config config, Y y)
{
y.SetConfig(config);
}
}
class Y
{
private readonly Z _z;
public Y(Z z)
{
_z = z;
}
public void SetConfig(Config config)
{
_z.SetConfig(config);
}
}
class Z
{
private Config _config;
public void SetConfig(Config config)
{
_config = config;
}
}
and
Bind<MetricsApiConfiguration>().To().Named("X1");
Bind<MetricsApiConfiguration>().To().Named("X2");
Any other (better) ideas?
WhenInjectedInto
is close, but as you noted, only looks at the current request. I created an extension method to address this exact use case:
public static IBindingInNamedWithOrOnSyntax<T> WhenAnyAncestorIs<T>(this IBindingWhenSyntax<T> binding, params Type[] types)
{
bool Matches(IRequest request)
{
Type target = request.Target?.Member?.ReflectedType;
return (target != null && types.Any(t => t == target))
|| (request.ParentRequest != null && Matches(request.ParentRequest));
}
return binding.When(Matches);
}
and use like:
Bind<IConfigSource>().To<DataConfigSource>()
.WhenAnyAncestorIs(
typeof(DataConfigurationRepository),
typeof(ManifestRepository),
typeof(DataManager)
)
.InSingletonScope();
you will need an additional binding for requests that do not use those Types.