Rob
Rob

Reputation: 5286

Providing Ninject with constructor dependencies it can't resolve?

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

Answers (2)

s3raph86
s3raph86

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

Marc
Marc

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

Related Questions