4

I am having a 2 controller PayerController and BusinessController. Both Controller constructors takes EntityManager as a parameter which is an abstract class. I would like to resolve Each Manager class depending on the controller I am using.

For PayerController I would like to inject PayerManager class and for BusinessController I would like to inject BusinessManager.

Currentlly I am getting the last Object that has been resolved with EntityManager i.e BusinessManager.

I remember in Ninject we can do conditional injection pretty easily.

This is how current I am resolving the dependency But wont work.

Startup.cs

services.AddScoped(typeof(EntityManager), typeof(PayerManager));
services.AddScoped(typeof(EntityManager), typeof(BusinessManager));

Controllers

public class PayerController
{
   private PayerManager Manager{get;}
   public PayerController(EntityManager entityManager)
   {
      Manager = entityManager as PayerManager;
   }
}

Manager Classes

public class PayerManager : EntityManager
{
    public void MyPayer()
    {
    }  
}

public class BusinessManager : EntityManager
{
    public void MyBusiness()
    {
    }
}

public abstract class EntityManager
{
    public string IAMCommon()
    {
        return "";
    }
}
maxspan
  • 13,326
  • 15
  • 75
  • 104
  • Currentlly I am getting the last Object that has been resolved with EntityManager i.e BusinessManager. – maxspan Aug 14 '17 at 01:18
  • How do you expect the contianer to know which concrete class you want here? – DavidG Aug 14 '17 at 01:19
  • That is my question are there any conditional based methods like in ninject there were helper method. You can resolve dependency based on the controller. – maxspan Aug 14 '17 at 01:20
  • 1
    The default container doesn't do anything particularly clever. If you want additional functionality then you should use something like Autofac or Ninject. – DavidG Aug 14 '17 at 01:22
  • asp.net core does not support ninject – maxspan Aug 14 '17 at 01:23
  • 1
    Then try Autofac, I know that does and I've been using it for years. – DavidG Aug 14 '17 at 01:27
  • And Ninject hasn't been updated for months it seems, looks like it's time to move on... – DavidG Aug 14 '17 at 01:29
  • I left ninject long time ago but now we used default IOC of asp.net core. Thats a business decision. I would love to use Autofac – maxspan Aug 14 '17 at 01:31
  • You really shouldn't use the default for anything other than basic apps. Even the official docs state *The default services container provided by ASP.NET Core provides a minimal feature set and is not intended to replace other containers.* – DavidG Aug 14 '17 at 01:32
  • 1
    Why do not you try making interface for each concrete class and Injecting interface in controller – Khalil Aug 14 '17 at 01:34
  • How would I do in Autofac – maxspan Aug 14 '17 at 02:34
  • "asp.net core does not support ninject". That's not true: https://stackoverflow.com/questions/32788637/continued-ninject-support-in-asp-net-mvc-6 – Steven Aug 14 '17 at 06:11
  • @maxspan Use their docs [Autofac](http://docs.autofac.org/en/latest/integration/aspnetcore.html) Also check their [bestPractices](http://docs.autofac.org/en/latest/best-practices/index.html) – Khalil Aug 14 '17 at 09:50

3 Answers3

3

I don't understand why you think you need conditional dependency injection in this situation because the solution to make it work is very simple.

You can change your controllers to inject the correct type of dependency that they need.

public class PayerController
{
   private PayerManager Manager { get; }

   public PayerController(PayerManager manager)
   {
      Manager = manager;
   }
}

public class BusinessController
{
   private BusinessManager Manager { get; }

   public BusinessController(BusinessManager manager)
   {
      Manager = manager;
   }
}

Then make sure both types are registered in the service container.

services.AddScoped<PayerManager>();
services.AddScoped<BusinessManager>();

UPDATE

A better way is to use interfaces and possibly an abstract generic controller.

Define your interfaces:

public interface IEntityManager { }
public interface IPayerManager : IEntityManager { }
public interface IBusinessManager : IEntityManager { }

Update your classes to implement the interfaces:

public abstract class EntityManager : IEntityManager 
{ 
    protected EntityManager() { }
}

public class PayerManager : EntityManager, IPayerManager 
{ 
    public PayerManager() : base() { }
}

public class BusinessManager : EntityManager, IBusinessManager 
{ 
    public BusinessManager() : base() { }
}

Then create a base controller class:

public abstract class EntityController<T> : Controller where T : class, IEntityManager
{
    protected(T manager)
    {
        Manager = manager
    }

    protected T Manager { get; }
}

Change your controllers to inherit from base controller:

public class PayerController : EntityController<IPayerManager>
{
   public PayerController(IPayerManager manager) : base(manager) { }
}

public class BusinessController : EntityController<IBusinessManager>
{
   public BusinessController(IBusinessManager manager) : base(manager) { }
}

And update the service register:

services.AddScoped<IPayerManager, PayerManager>();
services.AddScoped<IBusinessManager, BusinessManager>();
Brad
  • 4,493
  • 2
  • 17
  • 24
  • I want to use an abstract class or an interface not a concrete class. – maxspan Aug 14 '17 at 01:54
  • 1
    Then create interfaces for your manager classes and register those in the service provider. – Brad Aug 14 '17 at 01:57
  • I have updated my answer to show how to do it with interfaces and a generic base controller – Brad Aug 14 '17 at 02:20
  • 1
    @maxspan: Brad actually has a point here, but I think you should take a step back and ask yourself whether your design follows or violates the Liskov Substitution Principle. You can check this by asking: "Does PayerController break if it gets injected with a BusinessManager?". If te answer is 'yes', you are violating SRP and each manager should get its own abstraction. – Steven Aug 14 '17 at 06:15
1

Make interface for each concrete class and Injecting interface in the controller

Startup.cs

services.AddScoped<IPayerManager, PayerManager>();
services.AddScoped<IBusinessManager, BusinessManager>();

Controllers

public class PayerController
{
    private IPayerManager _entityManager{get;}
    public PayerController(IPayerManager entityManager)
   {
       _entityManager= entityManager;
   }
}


public class BusinessController
{
    private IBusinessManager _entityManager{get;}
    public BusinessController(IBusinessManager entityManager)
   {
       _entityManager= entityManager;
   }
}

Manager Classes

public class PayerManager : EntityManager,IPayerManager 
{
    public void MyPayer()
    {
    }
}

 public class BusinessManager : EntityManager,IBusinessManager 
 {
     public void MyBusiness()
     {
     }
 }

 public abstract class EntityManager
 {
     public string IAMCommon()
     {
         return "";
     }
 }

Interfaces

public interface IPayerManager
{
    void MyPayer();
}

public interface IBusinessManager
{
    void MyBusiness();
}
Khalil
  • 1,047
  • 4
  • 17
  • 34
0

@Maxspan, my suggestion:

Put all interfaces in Contracts folder and this folder inside your Models/Entities folder (Models/Entities folder - Contracts folder)

IEntityManager Interface

namespace WebApplication1.Entities.Contracts
{
    public interface IEntityManager
    {
        //Method signature only
        string IAMCommon();
    }
}

IBusinessManager Interface

namespace WebApplication1.Entities.Contracts
{
    public interface IBusinessManager : IEntityManager
    {
        //Method signature only
        void MyBusiness();
    }
}

IPayerManager Interface

namespace WebApplication1.Entities.Contracts
{
    public interface IPayerManager : IEntityManager
    {
        //Method signature only
        void MyPayer();
    }
}

BusinessManager Concrete Class

using System;
using WebApplication1.Entities.Contracts;

namespace WebApplication1.Entities
{
    public class BusinessManager : IBusinessManager
    {
        //Method signature in IEntityManager
        public string IAMCommon()  //Heritage in IBusinessManager (IBusinessManager : IEntityManager)
        {
            //Implement your code here.
            throw new NotImplementedException();
        }

        //Method signature in IBusinessManager
        public void MyBusiness() 
        {
            //Implement your code here.
            throw new NotImplementedException();
        }
    }
}

PayerManager Concrete Class

using System;
using WebApplication1.Entities.Contracts;

namespace WebApplication1.Entities
{
    public class PayerManager : IPayerManager
    {
        //Method signature in IEntityManager
        public string IAMCommon() //Heritage in IPayerManager (IPayerManager : IEntityManager)
        {
            //Implement your code here.
            throw new NotImplementedException();
        }

        //Method signature in IPayerManager
        public void MyPayer()
        {
            //Implement your code here.
            throw new NotImplementedException();
        }
    }
}

Startup class

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using WebApplication1.Entities;
using WebApplication1.Entities.Contracts;

namespace WebApplication1
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IBusinessManager, BusinessManager>();
            services.AddScoped<IPayerManager, PayerManager>();
        }
    }
}

BusinessController

using Microsoft.AspNetCore.Mvc;
using WebApplication1.Entities.Contracts;

namespace WebApplication1.Controllers
{
    public class BusinessController : Controller
    {
        private readonly IBusinessManager _businessManager;

        public BusinessController(IBusinessManager businessManager)
        {
            _businessManager = businessManager;
        }

        public IActionResult Index()
        {
            //Both methods in BusinessManager due to heritage (BusinessManager : IBusinessManager)
            _businessManager.IAMCommon(); 
            _businessManager.MyBusiness();

            return View();
        }
    }
}

PayerController

using Microsoft.AspNetCore.Mvc;
using WebApplication1.Entities.Contracts;

namespace WebApplication1.Controllers
{
    public class PayerController : Controller
    {
        private readonly IPayerManager _payerManager;

        public PayerController(IPayerManager payerManager)
        {
            _payerManager = payerManager;
        }

        public IActionResult Index()
        {
            //Both methods in PayerManager due to heritage (PayerManager : IPayerManager)
            _payerManager.IAMCommon(); 
            _payerManager.MyPayer(); 

            return View();
        }
    }
}