1

I couldn’t find any similar issue so I’m writing this post. There is sample controller with private field IBaseClass. Sample code looks like follows:

public class TmpController : Controller
    {
        private IBaseClass _baseClass;

        public TmpController()
        {
            _baseClass = new BaseClass(this);
        }   
    }

    public interface IBaseClass
    {
        //...
    }

    public class BaseClass : IBaseClass
    {
        protected TmpController TmpController;

        public BaseClass(TmpController tmpController)
        {
            TmpController = tmpController;
        }

        //IBaseClass implementation
    }

My question is; how to inject BaseClass object to the constructor of TmpController using Unity framework? I want to make my controller "slimmer". I want to put the logic about validation and preparing dataSource of my controls like comboBox etc. to different class. I try to make some kind of SOC in my .Web project in that very specific case, which will make my controller easier to read and maintain. I'm using approach one controller per one view but I met the case with very complex form. Currently I have controller with more than 3000 lines of code and it's hard to maintain so I want to do something with it. And yes I'm using Services and Repositories but the problem is about validation of ViewModels, mapping ViewModel objects into DTOs and backwards, preparing data source of given components etc.

  • Possible duplicate of [Specify constructor for the Unity IoC container to use](http://stackoverflow.com/questions/2162061/specify-constructor-for-the-unity-ioc-container-to-use) – Daniel Corzo Mar 10 '17 at 22:35
  • You have your constructors the wrong way round here. You need to be injecting `IBaseClass` into `TmpController`, not the other way round. – DavidG Mar 10 '17 at 22:49
  • #Daniel Corzo I think you are wrong. That link is typically about c# case not asp.net mvc and it is about different problem “Specify constructor for the Unity IoC container to use” – Razem Ponad-kilo Mar 10 '17 at 22:51
  • DavidG it's not that simple because of the BaseClass(TmpController tmpController) constructor. – Razem Ponad-kilo Mar 10 '17 at 22:56
  • @RazemPonad-kilo Yes, that's what I mean, do you really think that's a good idea? I certainly don't. – DavidG Mar 10 '17 at 23:02
  • Why you can't have controller dependent on IBaseClass instead of other way around? You can always pass the controller instance in the method of IBaseClass. – Chetan Mar 11 '17 at 03:16
  • @Chetan Ranpariya do you think about something like this: public class TmpController : Controller { private IBaseClass _baseClass; public TmpController(IBaseClass baseClass) { _baseClass = baseClass; } } public interface IBaseClass { void Method1(TmpController tmpController); void Method2(TmpController tmpController); } public class BaseClass : IBaseClass { public void Method1(TmpController tmpController) {} public void Method2(TmpController tmpController) {} } – Razem Ponad-kilo Mar 11 '17 at 12:46
  • It can be a solution of my problem but I see one minus. I need to put TmpController tmpController as one of parameters for each method of IBaseClass which is redundant. What do you think? – Razem Ponad-kilo Mar 11 '17 at 12:55
  • The other way I see is not to use DI but create static class with extension methods: public class TmpController : Controller { public ActionResult Index() { return View(this.Method1()); } } public static class BaseClass { public static IndexViewModel Method1(this TmpController tmpController) { return new IndexViewModel();} } public class IndexViewModel { } – Razem Ponad-kilo Mar 11 '17 at 13:13
  • But I'm not pretty sure if the second solution would be a good idea if we think about unit testing. – Razem Ponad-kilo Mar 11 '17 at 13:15
  • @RazemPonad-kilo please go thru the solution I posted below. I am sure that will resolve your issue. – Chetan Mar 11 '17 at 17:21

1 Answers1

0

@Razem, what you guess from my comment is correct. And the minus point you described is also valid.

What you are asking "Service depending on the controller" can surely be achieved but that would be a bad design.

Currently BaseClass is only dependent on TempController. How would you handle the scenario when you need the BaseClass in some other controller also? The code will start breaking and you will end up adding new dependency to BaseClass.

Also as per the design recommendations Top Layers should be dependent on the Bottom Layers not the vice versa.

Being said that, you can still achieve the feature you are looking for that too by making controller dependent on the IBaseClass.

I am not sure the specific reasons you need to access controller inside BaseClass. I have made certain assumptions while creating following suggestions. One of such assumption is BaseClass, IBaseClass and Controller classes are part of the same assembly.

//Have a BaseController Class with the properties and/or method which you will be using in the `BaseClass` class and make them virtual so that derived controller classes can override them to have specific implementation.

public class BaseController : Controller
{
    public virtual string ControllerMethod()
    {
        return "Controller Method from Base Controller";
    }

    public virtual string RandomValue
    {
        get
        {
            return "Random value from Base Controller";
        }
    }
}

Create a method in IBaseClass which will Set the Controller for it.

public interface IBaseClass
{
    void SetController(BaseController controller);

    void Method1();
}

public class BaseClass : IBaseClass
{
    private BaseController controller;
    public void SetController(BaseController controller)
    {
        this.controller = controller;
    }

    public void Method1()
    {
        var str = this.controller.RandomValue;
    }
}

And derive the TempController from the BaseController and make it dependent on IBaseClass. And in the constructor of TempController call SetController method of IBaseClass by passing this argument to it. You also can override method/properties of BaseController here.

After this you can call any method of IBaseClass without passing controller instance to it.

public class TempController : BaseController
{
    private IBaseClass baseClass;
    public HomeController(IBaseClass productService)
    {
        this.baseClass = productService;
        this.baseClass.SetController(this);
    }

    public override string RandomValue
    {
        get
        {
            return "Random value from Derived Class.";
        }
    }

    public ActionResult Index()
    {
        this.baseClass.Method1();

        ViewBag.Title = "Home Page";

        return View();
    }
}

Install nuget package Unit.Mvc in your web project. Open file Unity.Config located under App_Start folder and change method RegisterTypes as following.

public static void RegisterTypes(IUnityContainer container)
{
   container.RegisterType<IBaseClass, BaseClass>(new PerRequestLifetimeManager());
}

I am sure I don't need to explain how this is going to work.

P.S. : You need to make sure that you calls IBaseClass.SetController method in controller constructor to avoid NullReferenceException when you use controller in BaseClass. This is small overhead you need to take to achieve good and maintainable design.

Chetan
  • 6,711
  • 3
  • 22
  • 32