0

I have a class that might need a service or not, previously registered through dependency injection, depending on the user needs.

He instanciate my class via the static method GetInstance(bool), and if bool is set to true then I need to call a constructor based on dependency injection. How to achieve this?

public class MyClass {
  private MyClass() {
    // ...
  }

  private MyClass(MyService env) {
    // ...
  }

  public static MyClass GetInstance(bool serviceIsNeeded) {
     if (serviceIsNeeded) {
         /* How to realize the following ?
          * if (!ServiceRegistered<MyService>())
          *     throw new ServiceNotFoundException(...);
          * return InstanciateWithDependencyInjection(typeof(MyClass));
         */
     }
     else
         return new MyClass();
  }
}

For instance in Java, you put an @Inject tag on top of your constructor, and then you instanciate your class like this: MyClass myClass = getInjector(getContext()).getInstance(MyClass.class)

I'm looking for the same concept in C# and ASP.NET Core 2.

Liam
  • 27,717
  • 28
  • 128
  • 190
Antoine C.
  • 3,730
  • 5
  • 32
  • 56
  • 2
    This is the an anti pattern. Don't do this. If the class needs the dependency then inject it. If the dependency is relative then you likely need to refactor your code so that it's not (base class maybe?). Also injecting dependencies shouldn't have an overhead (not much anyway). Why does injecting the dependency and not using it make any difference? This seems like it could be a "code smell"? what does instantiating `MyService` do? – Liam Feb 27 '18 at 10:56
  • @Liam I'll try to refactor it then, but anyway, even if there is no condition in `GetInstance`, how to instanciate a class that needs DI? – Antoine C. Feb 27 '18 at 10:59
  • 2
    Related [Is ServiceLocator an anti-pattern?](https://stackoverflow.com/questions/22795459/is-servicelocator-an-anti-pattern) IMO yes – Liam Feb 27 '18 at 11:00
  • 2
    You don't it should be DI all the way down your stack. The framework (simple injector, whatever it is you're using, etc.) should instanciate the bottom class and resolve all the dependencies all the way up. This is how you maintain loose coupling. Once you start resolving dependencies using service locator (which is pretty much what you talking about here) you introduce a tightly coupled component. – Liam Feb 27 '18 at 11:03
  • @Liam ok got it, thank you – Antoine C. Feb 27 '18 at 11:04
  • for example, unit test that method? You can't because you can't inject `MyService` into `GetInstance` – Liam Feb 27 '18 at 11:04
  • @Liam for unit testing, I used to have one UnitTest project, which references a dummy web app that registers all the required services, so everything was injected when needed. Sounds bad? – Antoine C. Feb 27 '18 at 11:08
  • It's poor isolation. Are you testing the method or the DI or both? Really you want to keep you test small and concise as it aids with refactoring. If you change x you don't want it to impact the tests for y, etc. – Liam Feb 27 '18 at 11:16
  • 1
    @Liam I was testing the method, I just discovered Mocks so everything is good now, you can add a sum-up as an answer if you want I'll accept it, it led me to the good way – Antoine C. Feb 27 '18 at 14:31

2 Answers2

2

Tl;Dr I think your heading down the wrong road here. This is pretty much Service Locator (anti) pattern.


You should try an integrate DI all the way up and down with your DI framework building the bottom class then everything else just accepts dependencies. There's also an argument to say, "why does it matter if you inject a class and don't use it?". Providing that class has no overhead in construction (which it shouldn't) just inject it and use it.

So you class should look something like:

public class MyClass {

  //inject all the dependencies
  private MyClass(MyService env) {
    // ...
  }


}

Nice and simple.

Service locator tends to lead towards too close coupling of components. For example in your code in the question, how do you unit test GetInstance? It's tricky, you can't inject mocks, etc.

The whole point of DI is to decouple your components to allow for better flexibility and to ease unit testing.

I used to have one UnitTest project, which references a dummy web app that registers all the required services

See this isn't great testing, I'd say your test here will quickly become bloated and difficult to maintain. Where as the above is easy to test using a mocking engine, etc. If you change x you don't want it to impact the tests for y.

Bernard Vander Beken
  • 4,848
  • 5
  • 54
  • 76
Liam
  • 27,717
  • 28
  • 128
  • 190
-2

I would keep MyClass clear and put dependency in constructor. If this dependency cannot be resolved by constructor than I would create factory class that would manually create object. Something like this:

public class MyClassFactory{

    public MyClass Create(){
        // insetead of manualy creating depenedecy service locator can be 
        // used
        return new MyClass(new MyService());
    }
}

public class MyClass{

    public MyClass(MyService myService){}
}

public class MyService {

}
unarity
  • 2,339
  • 2
  • 21
  • 17
  • 1
    This isn't [dependency injection at all anymore](https://stackoverflow.com/questions/130794/what-is-dependency-injection). How do you unit test `MyClass`? What if MyService also has dependencies? What if these dependencies change? – Liam Feb 27 '18 at 15:05
  • You still unit test MyClass, and it doesn't change. MyClass has constructor dependency. It can be injected by DI or manually(like in example). In factory you can use service locator or do it manually, it is up to you. And answer to you questions is use DI and do not manually wire dependencies. But obviously there is need to use service locator(or manually wiring), so with these approach you have MyClass clean and you use factory to create object and to get dependecies. – unarity Feb 28 '18 at 08:46