The current code solves the issue I had when trying to use property injection.
Problem: Every module must use constructor injection because of a circular reference that occurs when not using constructor injection with my factories and trying to use property injection
Reason: A module requires a client and each client must have its own list of instantiated modules.
What I want: I want every module that derives from BaseModule
to be able to not declare a constructor and allow BaseModule
to have IIrcClient
injected via a property.
But I'm not sure how to use Ninject to correctly do this without getting a circular reference. Is there a way so that for each client, a new list of modules are injected into that client (where all modules then have an injected reference of the client they belong to).
.ToFactory()
is an extension method of Ninject which creates a proxy based on an interface that creates an object with a dependency.
public class BaseClient : IClient
{
private readonly IEnumerable<IModule> modules;
protected BaseClient(IModuleFactory moduleFactory)
{
// each client has its own instances of modules set
// each module needs a reference to the client
this.modules = moduleFactory.Create(this);
}
}
public class BaseModule : IModule
{
private readonly IIrcClient ircClient;
// Factory will inject and create
protected BaseModule(IIrcClient ircClient)
{
this.ircClient = ircClient;
}
}
public class Bot
{
// Injected factory
public Bot(Context context, IrcClientFactory factory)
{
this.factory = factory;
this.context = context;
}
public void Start()
{
List<Network> networks = context.Networks.ToList();
foreach (var network in networks)
{
// create new client...but also
// new client gets injected with module factory
// module factory then populates modules based on client
IIrcClient ircClient = this.ircClientFactory.Create();
ircClient.Connect(network);
this.clients.Add(ircClient);
}
}
}
public class Program
{
public void Main()
{
kernel.Bind<IIrcClient>().To<DefaultIrcClient>();
kernel.Bind<IIrcClientFactory>().ToFactory();
kernel.Bind<IModule>().To<DiceModule>().WithPropertyValue("Name", "Dice Module");
kernel.Bind<IModule>().To<ModuleOne>().WithPropertyValue("Name", "ModuleOne");
kernel.Bind<IModule>().To<RubyModule>().WithPropertyValue("Name", "Ruby Module");
kernel.Bind<IModule>().To<AdminModule>().WithPropertyValue("Name", "Admin Module");
// Module Factory will generate IEnumerable<IModule>
kernel.Bind<IModuleFactory>().ToFactory();
var bot = kernel.Get<Bot>();
bot.Start();
}
}
A possible fix, but I would have to manaually set the property instead of letting Ninject inject the property:
public class BaseClient : IClient
{
private readonly IEnumerable<IModule> modules;
protected BaseClient(IModuleFactory moduleFactory)
{
// let the factory create new modules
// but dont pass the instance of the client
this.modules = moduleFactory.Create();
foreach(var module in modules)
{
module.IrcClient = this; // don't want to do this, but I think it would fix it
}
}
}
This way, I could create tons of modules without needing to always implement the base constructor and pass in the IClient
dependency:
public class ChildModule : BaseModule
{
// client was injected via property
// override virtual methods as required.
}