0

I am new to dependency injection, and I am trying to solve an issue. I have two services. Each of these services have methods who need eachother.

For instance: SiteManager have methods where it needs my ForumManager. My ForumManager have methods where it needs my SiteManager.

I have the following two classes:

public class SiteManager:ISiteManager
{
    public IForumManager ForumManager { get; set; }

    public SiteManager()
    {
        this.ForumManager = new ForumManager();
    }
}

public class ForumManager:IForumManager
{
    public ISiteManager SiteManager { get; set; }

    public ForumManager()
    {
        this.SiteManager = new SiteManager();
    }
}

Very obviously this will result in a stack overflow exception, as they call eachother. I've read a lot of posts here, and I think I just need a small hint on how to solve this. I have my interfaces in their own assembly.

I thought about putting the dependencies in their own property so when they are used, they are made. However, is this best practice?

I do not use an IoC container (and I haven't used that before).

Any hints on how to solve this particular issue in a "best practice" way! :-)

Lars Holdgaard
  • 8,386
  • 20
  • 85
  • 170
  • A "*Manager" instantiated with no parameters sounds like something unlikely to require multiple instances. Have you considered making them singleton objects and bypassing the issue entirely? – Phoshi Aug 29 '13 at 08:05
  • How about using inheritance have a manger class that has all your common methods ? – TheKingDave Aug 29 '13 at 08:05
  • 1
    It sounds like you have a misplaced responsibility. Why does ForumManager need a reference to SiteManager? And vice versa? – MattDavey Aug 29 '13 at 08:12
  • Agree with MattDavey. Why are they dependent of each other? – jgauffin Aug 29 '13 at 14:16

5 Answers5

3

You should not be calling new within your classes, that will tightly couple them. The correct pattern for IOC that will allow you to test each class separately using mocks is:-

public class SiteManager:ISiteManager
{
    private readonly IForumManager forumManager;

    public SiteManager(IForumManager forumManager)
    {
        this.forumManager = forumManager;
    }
}

public class ForumManager:IForumManager
{
   private readonly ISiteManager siteManager;

   public ForumManager(ISiteManager siteManager)
   {
      this.siteManager = siteManager;
   }
}

But, that doesn't solve the mutual recursion. The easiest way to solve that is to not use constructor injection for one of the classes, but use property injection instead, i.e. put the SiteManager back to a public property on the ForumManager and set it after creating both objects.

Your setup code then does:-

     IForumManager forumManager = new ForumManager();
     ISiteManager siteManager = new SiteManager(forumManager);
     forumManager.SiteManager = siteManager;

Another alternative would be to pass a ForumManagerFactory into the SiteManager, e.g. a Func<ISiteManager,IForumManager>.

     ISiteManager siteManager = new SiteManager((s) => new ForumManager(s));

Inside the site manager you can then call the Func, passing this to get the IForumManager. The ForumManager gets an instance of the SiteManager and the SiteManager has the ForumManager object.

Ian Mercer
  • 35,804
  • 6
  • 87
  • 121
0

what you are doing and what you are suggesting both will cause stack overflow exception. i don't know why will you want to do something like that and you are not giving any hints on that but i guess i can offer you to create a manager, maybe a singleton, maybe just with static method and do:

public static void DoStuff(ISiteManager sm, IForumManager fm)
{
  // your code here can use the best of both without SO
}

and not holding ISiteManager in ForumManager and IForumManager in SiteManager

No Idea For Name
  • 10,935
  • 10
  • 37
  • 61
0

You clearly can't have interdependant classes. What you need to do is to create a separate class and move there the methods which use the same time forum manager and sitemanger: Here is a sample 3rd class:

class ForumAndSiteManager
{
     public ForumAndSiteManager(ISiteManager siteMaanger, IForumManager forumManager)
     {
         //save the injected object to private fileds
     }

     //define methods which will use both sitemanager and forum manager
}

This way you will brake the circular depedency

Alexandr Mihalciuc
  • 2,477
  • 13
  • 12
0

When using MVP with winforms and AutoFac, I had this exact same issue with the view referencing the presenter and the presenter referencing the view. The way I got around it is to have one of your classes pass itself to the other using an Initialize method. I am not sure if this is best practice, but I have seen it suggested before (this question about mvp)

So for the implementation details:

public class SiteManager:ISiteManager
{
    public IForumManager ForumManager { get; set; }

    public SiteManager()
    {

    }

    public Initialize(IForumManager forumManager)
    {
        ForumManager = forumManager
    }
}

public class ForumManager:IForumManager
{
    public ISiteManager SiteManager { get; set; }

    public ForumManager(ISiteManager siteManager)
    {
        this.SiteManager = new SiteManager();
        this.SiteManager.Initialize(this);
    }
}

Edit Actually would probably go with the other solutions posted, I was just looking at this purely from a circular dependency point of view

Community
  • 1
  • 1
Melvin DVaz
  • 1,214
  • 6
  • 5
0

You should definitely avoid circular dependencies. I mean A depends on B and B on A.

This is like a cancer of your context dependencies. We used spring.net framework which contrary to java's version is unable to switch on a failing system if it discovers this kind of dependency. I have to say that this brings us only mess and hours of spring's logs searching and analyzing.

We defined almost 200 without any problem but once we added just another bean along with Lazy reference it failed down. This is almost impossible to untangle our solution to avoid it right now so we hook and hook and hook whilst it fails :-(

Martin Podval
  • 1,047
  • 7
  • 14