Search code examples
c#asp.net-coredependency-injectionlockinginversion-of-control

Dependency Injection: setting and sharing properties of scoped service in ASP.NET Core


I want to set property in some injected service in controller for later abusing it when this service will be injected in other place during the same request, so I expect this property not to change as far as service is injected as Scoped.

Is that safe approach or how would you suggest to achive such a behaviour?

MyController(IServiceWithProperty serviceWithProperty) {
  _serviceWithProperty = serviceWithProperty;
}

public IActionResult Get(int settingToSet) {
  _serviceWithProperty.SetProperty(settingToSet);
  return Ok(_anotherService.GetSomething()); 
}

And as I said AnotherService injects ServiceWithProperty as well.

public class AnotherService : IAnotherService {
  public AnotherService(IServiceWithProperty serviceWithProperty) {
    _serviceWithProperty = serviceWithProperty;
  }

  public string GetSomething() {
    int prop = _serviceWithProperty.GetProperty(); //here I expect to get property which has been set in controller, would that work and is it fine to do it like that?
  } 
}

Solution

  • The interaction between those two services is going to be hard to follow once the size of the code base grows. Even given this simple example (good job of reducing the problem to its essence, BTW), I had to look at the code for a few minutes to understand what's going on.

    Besides, a design like this seems to be close to violating the Liskov Substitution Principle (LSP), because it'll only work when specific, concrete implementations are in use. According to the LSP, you should be able to exchange one subtype with another without changing the correctness of the system. Is this possible here? Can you replace the IServiceWithProperty implementation you have in mind with another implementation that does nothing?

    Instead, follow the Dependency Inversion Principle, from which it follows that clients should define the abstraction. So, if MyController requires an abstraction that can translate an int to something else, then that's the abstraction it requires:

    MyController(ITranslator translator) {
      _translator = translator;
    }
    
    public IActionResult Get(int setting) {
      return Ok(_translator.Translate(setting)); 
    }
    

    The next step, then, is to figure out how to implement the ITranslator interface.