2

I am working on a project with a complex business. Consider two classes : AccountService and SchoolService

I am using Unity and the dependency resolver of the Web API to implement dependency injection in the constructor.

The school service uses the account service in some methods, also the account service uses the school service. All this is required in the business of the project. This will cause circular dependency, and it is not possible to move the methods from one class into another.

Can you please provide any idea on how to solve this ?

Here is an example:

public class SchoolBLC : ISchoolBLC
{
    public School GetSchool(int schoolId)
    {
        ...
    }

    public bool RenewRegistration(int accountId)
    {
        bool result = true;

        IAccountBLC accountBLC = new AccountBLC();
        // check some properties related to the account to decide if the account can be renewed
        // ex : the account should not be 5 years old
        // check the account created date and do renewal

        return result;
    }
}

public class AccountBLC : IAccountBLC
{
    public void ResetAccount(int accountId)
    {
        ISchoolBLC schoolBLC = new SchoolBLC();
        School accountSchool = schoolBLC

        // get the school related to the account to send a notification 
        // and tell the school that the user has reset his account
        // reset account and call the school notification service
    }

    public Account GetAccount(int accountId)
    {
        ...
    }
}

The two classes are referencing each other, this is the situation for 70% of the BLCs in the project.

Jek
  • 441
  • 7
  • 16
  • Can u give an example of such circular dependency in your project. – user1849310 Oct 17 '16 at 19:40
  • 4
    Sounds like a poor design... I'd break out the common stuff to a 3rd service. That'll solve the circular dependency. DI engines usually will throw an exception on a circular reference. – SledgeHammer Oct 17 '16 at 19:49
  • @SledgeHammer More to the point. How would you solve the problem even without DI? DI isn't magical, if you can't do it without it, you can't do it with it. – Aron Oct 18 '16 at 05:13
  • I would like to add to SledgeHammer's answer. Your problem is that you have two classes which are tightly coupled. If they are 70% coupled, does that not mean they are the same unit of code? Consider the core principle of OOP, encapsulation; you should expose as little functionality as possible. When you have two classes with methods that are only called by each other, YOU ARE NOT DOING ENCAPSULATION. Which means, your code is not OOP. – Aron Oct 18 '16 at 05:18
  • @SledgeHammer can you please explain how i can fix the design, and what the 3rd service should hold. – Jek Oct 18 '16 at 05:20
  • 1
    @Aron. No I'm not. I'm suggesting that if interface A & B have a lot of common code / functionality that it should either be in a base class or broken out into interface C. Then A & B use interface C instead of B & A and you break the circular reference. – SledgeHammer Oct 18 '16 at 14:14

2 Answers2

0

If you absolutely have to do it that way you can have an interface that does your IoC logic and resolve that to an implementation that wraps Unity's resolution, e.g.

public interface ITypeResolver
{
    T Resolve<T>();
}

Then you can pass that interface to both services in the constructor and use it to lazy-resolve the other service before you use it, outside the constructor.

That way when both services are initialized they will not have a direct dependency on the other service, only on ITypeResolver

KMoussa
  • 1,568
  • 7
  • 11
0

I will do as suggested by @KMoussa but with some modifications:

The project is using the anemic model, so i will use a context pattern to lazy load and create any service, and the context will be passed as parameter to the service constructor.

public class SDPContext : ISDPContext
{
    private ITypeResolver _typeResolver;

    public Account CurrentUser { get; set; }

    public IAccountService AccountService
    {
        get
        {
            // lazy load the account service
        }
    }

    public ISchoolService SchoolService
    {
        get
        {
            // lazy load the schoolservice
        }
    }

    public SDPContext(ITypeResolver typeResolver)
    {
        this._typeResolver = typeResolver;
    }
}

public class ServiceBase
{
    public ISDPContext CurrentContext { get; set; }

    public ServiceBase(ISDPContext context)
    {
        this.CurrentContext = context;
    }
}

public class AccountService : ServiceBase, IAccountService
{
    public AccountService(ISDPContext context) : base(context)
    {

    }

    public bool ResetAccount(int accountId)
    {
        // use base.Context.SchoolService to access the school business
    }
}

public class SchoolService : ServiceBase, ISchoolService
{
    public SchoolService(ISDPContext context) : base(context)
    {
        //this._accountService = accountService;
    }

    public void RenewRegistration(int accountId)
    {
        // use the base.Context.Account service to access the account service
    }
}
Jek
  • 441
  • 7
  • 16