简体   繁体   中英

DI Constructor Injection Neatness

Okay so I am looking for some specific tips about Dependency Injection and how to use it.

Basically I have a MVC website which uses Ninject (and the Ninject MVC package). So when I am creating the MVC page I use constructor injection in the controllers. This is okay but IMO a bit 'ugly', but the main thing I don't like is having to pass all of the injected repositories into the other classes, it just seems a bit OTT having to pass like 6 repositories-8 repositories into a static method or object constructor.

Not to mention that on some of my pages I am having to work on almost every repository so the controller consructor gets huge and not the most manageable.

Are there any other options that wont clutter my code up as much? I don't really want to pass them in as single 'setting' objects either as that just moves the problem to a different line of code.

I also use the same class libraries for console/desktop applications. I like the idea of using DependencyResolver.Current in the class libraries but everyone says this is an anti-pattern and constructor injection should be used.

Maybe have an MyProjectDIContext class which has a dictionary which i can populate with the injected types in the controller constructors then pass the context to all methods as needed?

I have had a look for the answer bit I can't quite seem to find something that fits well.

One of the great things about Constructor Injection is that it makes design and maintainability problems more obvious. When using constructor injection, it becomes very easy to see the amount of dependencies a class has, whereas without constructor injection, a class still has the same number of dependencies, but they are tucked away.

The problem you are seeing is called Constructor Over-injection and it's a design smell, because it indicates that you are violating the Single Responsibility Principle (SRP). The SRP guides in keeping your classes small, focussed and most of all: maintainable.

Are there any other options that wont clutter my code up as much?

Absolutely: make smaller classes. MVC controllers typically get huge when we use it to group methods of a certain concept, such as 'customer' or 'order'. This however means that a controller is an ever growing class that has to be changed for any new feature that arrives. This is a violation of the Open/closed Principle that tells we should strive to have a system where we can plugin new features without having to touch existing classes.

The solution therefore is not to revert to the Service Locator anti-pattern or Property Injection, but to create smaller classes that do one particular thing. Constructor Injection should be your primary way of applying Dependency Injection, even in class libraries.

It seems that your controller is shuffling repositories to other classes. Let Ninject supply those classes to you instead:

public class Controller
{
    public Controller(IDependencyFactory dependency) { }
}

public interface IDependencyFactory
{
    IDependency CreateDependency();
}

public interface IDependency
{
}

public class Dependency : IDependency
{
    public Dependency() { }
}

public class Program
{
    public static void Main()
    {
        var standardKernel = new StandardKernel();
        standardKernel.Bind<IDependencyFactory>().ToFactory();
        standardKernel.Bind<IDependency>().To<Dependency>();
    }
}

You don't have to write an implementation of IDependencyFactory , that is handled by the factory extension. Your Dependency -class will get it's dependencies injected by Ninject.

Okay I seem to have found a way to do this which doesn't seem as smelly!

I had thought that if you are for example using MVC you would get the Repositories in through the constructor and that was the only way to resolve the dependencies.

After doing some work to move sections of reusuable code into IoC friendly Interfaces and their implementations I noticed that when you create reference IMyInterface in a controller constructor the default constructor of the implementing class is run and you can pull other IoC classes such as resolved repositories from there.

This may be common knowledge but it does make things much much neater and solves the problem I was having.


Example

Controller

public IDefaultTemplateManager DefaultTemplateManager { get; set; }

public DefaultController(IDefaultTemplateManager defaultTemplateManager) {
    this.DefaultTemplateManager = defaultTemplateManager;
}

public ActionResult MyAction(FormCollection collection) {
    DefaultTemplateManager.Process("MyKeyHere");
    View();
}

IDefaultTemplateManager

public interface IDefaultTemplateManager {
    ProcessResponse Process(string UniqueKey, DefaultTemplateManagerEditMode DatabaseName, string DefaultTemplateName);
}

DefaultTemplateManager

    public class DefaultTemplateManager : IDefaultTemplateManager {

        protected IRepository<MyEntity1> MyEntityRepo1 { get; set; }
        protected IRepository<MyEntity2> MyEntityRepo2 { get; set; }
        protected IRepository<MyEntity3> MyEntityRepo3 { get; set; }
        protected IRepository<MyEntity4> MyEntityRepo4 { get; set; }
        protected IRepository<MyEntity5> MyEntityRepo5 { get; set; }
        protected IRepository<MyEntity6> MyEntityRepo6 { get; set; }

        public DefaultTemplateManager(IRepository<MyEntity1> MyEntityRepository1, IRepository<MyEntity2> MyEntityRepository2, IRepository<MyEntity3> MyEntityRepository3, IRepository<MyEntity4> MyEntityRepository4, IRepository<MyEntity5> MyEntityRepository5, IRepository<MyEntity6> MyEntityRepository6, ) {
            this.MyEntityRepo1 = MyEntityRepository1;
            this.MyEntityRepo2 = MyEntityRepository2;
            this.MyEntityRepo3 = MyEntityRepository3;
            this.MyEntityRepo4 = MyEntityRepository4;
            this.MyEntityRepo5 = MyEntityRepository5;
            this.MyEntityRepo6 = MyEntityRepository6;
        }

        public ProcessResponse Process(string UniqueKey) {
            /* Do Work */
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM