There are several questions on Stack Overflow that are similar but not exactly what I'm looking for. I would like to do Ninject binding based on a runtime condition, that isn't pre-known on startup. The other questions on Stack Overflow for dynamic binding revolve around binding based on a config file or some such - I need to it to happen conditionally based on a database value while processing the data for a particular entity. E.g.,
public class Partner
{
    public int PartnerID { get; set; }
    public string ExportImplementationAssembly { get; set; }
}
public interface IExport
{
    void ExportData(DataTable data);
}
Elsewhere, I have 2 dlls that implement IExport
public PartnerAExport : IExport
{
    private readonly _db;
    public PartnerAExport(PAEntities db)
    {
        _db = db;
    }
    public void ExportData(DataTable data)
    {
        // export parter A's data...
    }
}
Then for partner B;
public PartnerBExport : IExport
{
    private readonly _db;
    public PartnerBExport(PAEntities db)
    {
        _db = db;
    }
    public void ExportData(DataTable data)
    {
        // export parter B's data...
    }
}
Current Ninject binding is;
public class NinjectWebBindingsModule : NinjectModule
{
    public override void Load()
    {
        Bind<PADBEntities>().ToSelf();
        Kernel.Bind(s => s.FromAssembliesMatching("PartnerAdapter.*.dll")
                          .SelectAllClasses()
                          .BindDefaultInterfaces()
                   );
    }
}
So how do I set up the bindings such that I can do;
foreach (Partner partner in _db.Partners)
{
    // pseudocode...
    IExport exportModule = ninject.Resolve<IExport>(partner.ExportImplementationAssembly);
    exportModule.ExportData(_db.GetPartnerData(partner.PartnerID));
}
Is this possible? It seems like it should be but I can't quite figure how to go about it. The existing binding configuration above works fine for static bindings but I need something I can resolve at runtime. Is the above possible or am I just going to have to bypass Ninject and load the plugins using old-school reflection? If so, how can I use that method to resolve any constructor arguments via Ninject as with the statically bound objects?
UPDATE: I've updated my code with BatteryBackupUnit's solution such that I now have the following;
Bind<PADBEntities>().ToSelf().InRequestScope();
Kernel.Bind(s => s.FromAssembliesMatching("PartnerAdapter.*.dll")
                    .SelectAllClasses()
                    .BindDefaultInterfaces()
                    .Configure(c => c.InRequestScope())
            );
Kernel.Bind(s => s.FromAssembliesMatching("PartnerAdapter.Modules.*.dll")
                    .SelectAllClasses()
                    .InheritedFrom<IExportService>()
                    .BindSelection((type, baseTypes) => new[] { typeof(IExportService) })
            );
Kernel.Bind<IExportServiceDictionary>().To<ExportServiceDictionary>().InSingletonScope();
ExportServiceDictionary dictionary = KernelInstance.Get<ExportServiceDictionary>();
Instantiating the export implementations within 2 test modules works and instantiates the PADBEntites context just fine. However, all other bindings in my services layer now no longer work for the rest of the system. Likewise, I cannot bind the export layer if I change PADBEntities variable/ctor argument to an ISomeEntityService component. It seems I'm missing one last step in configuring the bindings to get this work. Any thoughts?
Error: "Error activating ISomeEntityService. No matching bindings are available and the type is not self-bindable"
Update 2: Eventually got this working with a bit of trial and error using BatteryBackupUnit's solution though I'm not too happy with the hoops to jump thought. Any other more concise solution is welcome.
I changed the original convention binding of;
        Kernel.Bind(s => s.FromAssembliesMatching("PartnerAdapter.*.dll")
                          .SelectAllClasses()
                          .BindDefaultInterfaces()
                   );
to the much more verbose and explicit;
Bind<IActionService>().To<ActionService>().InRequestScope();
Bind<IAuditedActionService>().To<AuditedActionService>().InRequestScope();
Bind<ICallService>().To<CallService>().InRequestScope();
Bind<ICompanyService>().To<CompanyService>().InRequestScope();
//...and so on for 30+ lines
Not my favorite solution but it works with explicit and convention based binding but not with two conventions. Can anyone see where I'm going wrong with the binding?
Update 3: Disregard the issue with the bindings in Update 2. It appears that I've found a bug in Ninject relating to having multiple binding modules in a referenced library. A change in module A, even though never hit via breakpoint will break a project explicitly using a different module B. Go figure.
It's important to note that while the actual "condition match" is a runtime condition, you actually know the possible set of matches in advance (at least on startup when building the container) - which is evidenced by the use of the conventions. This is what the conditional / contextual bindings are about (described in the Ninject WIKI and covered in several questions). So you actually don't need to do the binding at an arbitrary runtime-time, rather you just have to do the resolution/selection at an arbitrary time (resolution can actually be done in advance => fail early).
Here's a possible solution, which features:
IExports)IExport at an arbitrary runtime.
internal interface IExportDictionary
{
    IExport Get(string key);
}
internal class ExportDictionary : IExportDictionary
{
    private readonly Dictionary<string, IExport> dictionary;
    public ExportDictionary(IEnumerable<IExport> exports)
    {
        dictionary = new Dictionary<string, IExport>();
        foreach (IExport export in exports)
        {
            dictionary.Add(export.GetType().Assembly.FullName, export);
        }
    }
    public IExport Get(string key)
    {
        return dictionary[key];
    }
}
Composition root:
// this is just going to bind the IExports.
// If other types need to be bound, go ahead and adapt this or add other bindings.
kernel.Bind(s => s.FromAssembliesMatching("PartnerAdapter.*.dll")
        .SelectAllClasses()
        .InheritedFrom<IExport>()
        .BindSelection((type, baseTypes) => new[] { typeof(IExport) }));
kernel.Bind<IExportDictionary>().To<ExportDictionary>().InSingletonScope();
// create the dictionary immediately after the kernel is initialized.
// do this in the "composition root".
// why? creation of the dictionary will lead to creation of all `IExport`
// that means if one cannot be created because a binding is missing (or such)
// it will fail here (=> fail early).
var exportDictionary = kernel.Get<IExportDictionary>(); 
Now IExportDictionary can be injected into any component and just used like "required":
foreach (Partner partner in _db.Partners)
{
    // pseudocode...
    IExport exportModule = exportDictionary.Get(partner.ExportImplementationAssembly);
    exportModule.ExportData(_db.GetPartnerData(partner.PartnerID));
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With