Reputation: 5286
Disclaimer: I'm quite new to DI and IoC, please forgive any drastic misunderstandings.
Consider a ClassB
that requires a object implementing IClassA
. Ninject should be able to inject instances of ClassA
into the constructor of ClassB
, assuming it can construct instances of ClassA
:
public class ClassA : IClassA
{
public ClassA(string runtimeDependency) { /* ... */ }
}
public class ClassB : IClassB
{
public ClassB(IClassA depA) { /* ... */ }
}
public sealed class TestBootstrapModule : NinjectModule
{
public override void Load()
{
Bind<IClassA>().To<ClassA>();
Bind<IClassB>().To<ClassB>();
}
}
Now, let's say some runtime logic is involved in deriving the string runtimeDependency
provided to ClassA
. How should I provide Ninject with runtimeDependency
so that it can provide ClassB
with instances of ClassA
?
The string will only be determined once, so I don't need to worry about injecting a new value into each instance.
Upvotes: 1
Views: 408
Reputation: 566
There are a few options here depending on your design, and specific problem. The first, easiest solution is to just provide the value when you request your service from Ninject
Kernel.Get<IClassA>("runtimeDependencyValue");
If this is not possible however, things get a bit more interesting. The way I've solved this previously is to actually create contextual bindings to System.String
itself.
Say if I want to bind a connection string, I'll create a custom attribute:
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class ConnectionStringAttribute : Attribute
{
/// <summary>
/// Denotes the setting that you want to populate the given property with.
/// </summary>
public string SettingName { get; private set; }
public ConnectionStringAttribute(string configSettingName = "")
{
SettingName = configSettingName;
}
}
and then I decorate my service constructor like this:
public class ClassA : IClassA
{
public ClassA([ConnectionString("AppDB")] string runtimeDependency) { /* ... */ }
}
Finally, my binding will look something like this:
Bind<string>()
.ToMethod(ctx =>
{
var attr = (ConnectionStringAttribute)context.Request.Target.GetCustomAttributes(typeof(ConnectionStringAttribute), true).First();
string settingName = string.IsNullOrEmpty(attr.SettingName) ? context.Request.Target.Name : attr.SettingName;
return ConfigurationManager.ConnectionStrings[settingName].ConnectionString;
})
.WhenTargetHas<ConnectionStringAttribute>();
You get the idea. Hope this helps :)
Upvotes: 0
Reputation: 9354
One way to do it is to provide the ClassA
via a method. Also keep in mind that with Ninject 2, you don't need modules and can do bindings directly in the Kernel.
Bind<IClassA>().ToMethod(_ =>
{
// do something interesting with a runtimeDependancy
return new ClassA(someInterestingVariable);
});
I'm really taking a stab as to when your runtime variable is available and it's scope.
Upvotes: 2