11
\$\begingroup\$

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.
}
\$\endgroup\$
6
  • \$\begingroup\$ Each DI has its pros and cons. Ninject, although very popular, may not be the best option for your situation. Have you tried any others? \$\endgroup\$
    – stink
    Dec 7, 2013 at 20:41
  • \$\begingroup\$ Thanks for the comment, no i have not, though apparently AutoFac is a good alternative. \$\endgroup\$ Dec 7, 2013 at 21:30
  • \$\begingroup\$ I'm not sure... but I do primarily use AutoFac. \$\endgroup\$
    – stink
    Dec 8, 2013 at 2:28
  • \$\begingroup\$ Have you considered perhaps you could make it Laxy: Either with actual Lazy<T> msdn.microsoft.com/en-us/library/dd997286.aspx Or by instead of depending on being passed the object, ask to be passed a Process<T> ()=> new T() \$\endgroup\$ Jan 13, 2014 at 7:59
  • 3
    \$\begingroup\$ Could you be more explicit about what "circular reference" you are getting with your attempted/preferred solution? \$\endgroup\$
    – ChrisW
    Feb 27, 2014 at 11:05

1 Answer 1

10
\$\begingroup\$

Your question isn't crystal-clear about what it is exactly that your code is supposed to be doing. We have to infer the functionality from this block of code:

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);
    }
}

This code involves [what appears to be] private fields that you haven't included. I'll assume the following:

private readonly Context context;
private readonly IrcClientFactory factory;

If I read this correctly, your Bot class is depending on concrete types; they should be abstractions, like IContext and IIrcClientFactory (the latter being an abstract factory).

Naming - Private Fields

This might be of personal preference, but I like my private fields prefixed with an underscore. Having _context and _factory removes the ambiguity in the constructor and then the this qualifier becomes redundant:

public Bot(Context context, IrcClientFactory factory)
{
    _factory = factory
    _context = context
}

The comment // injected factory is a useless one; prefer XML comments for that purpose:

 /// <summary>
 /// Creates a new instance of a Bot.
 /// </summary>
 /// <param name="context">The data context.</param>
 /// <param name="factory">A factory that creates IrcClient instances.</param>
 public Bot(Context context, IrcClientFactory factory)
 {
     _factory = factory
     _context = context
 }

As a bonus, you'll get instant IntelliSense support for your constructor and its parameters.

Back to the foreach loop; the code you have here doesn't seem to be using the fields you've initialized in your constructor:

IIrcClient ircClient = this.ircClientFactory.Create();

I would have expected:

IIrcClient ircClient = this.factory.Create();

I would rewrite it like this:

var client = _factory.Create();

Implicit Typing

The abstract factory shouldn't be revealing the implementation of its product to its client; the factory's Create() method should be returning an implementation for IClient, and shouldn't have to even know what possible implementations the factory can spit out. Your code is expecting an IIrcClient which is an abstraction, but from the rest of your code I infer that IIrcClient extends the IClient interface. Bottom line, the code doesn't care what type is returned, and it should be working against all implementations of IClient (assuming Connect(Network) is a member of IClient).

Implicit typing not only makes your code shorter and easier to read, it also emphasize that you don't really care what the actual type is.

After creating a client, the loop's body passes the network to the client through its Connect method, and then the client gets added to yet another unmentioned field, assuming:

private readonly _clients = new List<IClient>();`
public void Start()
{
    var networks = context.Networks.ToList();
    foreach (var network in networks)
    {
        var client = _factory.Create();
        client.Connect(network);
        _clients.Add(client);
    }
}

If the network parameter is passed into a field of the client, and used anywhere other than in the Connect method, it would probably be best to constructor-inject the network into the client inside the factory method. If it's passed into a field of the client but isn't used anywhere other than the Connect method, it would be best to constructor-inject the network into the client inside the factory method; what I'm trying to get to, is that constructor injection should always be prioritized over other forms of injections, whenever possible. In this case, I'd probably call the IClient implementation something like ConnectedClient and inject the network through the Create method, which returns an IClient with a connection, as the ConnectedClient implementing class' name says.


Reviewing the rest of this code is very hard, I'm not seeing all the pieces of the puzzle that I'd like to see in order to be confident in anything else I'd have to say.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for this answer, to be truthful I've been very busy with other work related things but eventually I'll come round to this since I asked this some time ago. I do appreciate this so give me some time :) \$\endgroup\$ Mar 3, 2014 at 23:04
  • \$\begingroup\$ I recall i ended up changing my complete architecture so that its based on a request / response pattern rather than associating and injecting dependencies like i am (from my sketchy memory) \$\endgroup\$ Mar 3, 2014 at 23:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.