0

In Blazor, automatic dependecy injection via constructor works as follows:

// ---- Method A: AUTOMATIC SERVICE DI ----//
IMyOtherService _otherService;
public class MyService(IMyOtherService oService) {
    _otherService = oService;
}

public async Task DoSomethingFromOtherService() {
    await _otherService.DoSomething();
}

The services are registered as follows:

services.AddSingleton<MyOtherService>();
services.AddSingleton<MyService>();

The above structure works fine and services are created when first used by the app.

It is also possible to inject the IServiceProvider to MyService and retrieve the required IMyOtherService WHEN NECESSARY as follows:

// ---- Method B: IServiceProvider INJECTION ----//
IServiceProvider _serviceProvider;
public class MyService(IServiceProvider serviceProvider) {
    _serviceProvider = serviceProvider;
}

public async Task DoSomethingFromOtherService() {
    await serviceProvider.GetService<IMyOtherService>().DoSomething();
}

// --- register services as follows:
services.AddSingleton<MyOtherService>();
services.AddSingleton<MyService>(provider => new MyService(provider));

I see a few advantages of Method B over Method A:

  • we can retrieve services when required on the fly without having to modify constructors,
  • (code readability)no need to write long constructors if there are many dependencies.

The drawback of Method B is that we need to ensure the service exists, which Method A does implicitly when actually creating the service instances.

I have the intuition that Method B is a way better tradeoff though. As it is NOT standard in the doc, I am wondering why it is not the suggested way. Is there a specific reason I am missing towrads NOT TO USE METHOD B?

neggenbe
  • 1,697
  • 2
  • 24
  • 62
  • 2
    "provided we register the services in the correct order" - nonsense. The order does not matter at all. – H H Oct 13 '21 at 13:09
  • You are right - services are not sensitive to order if added with `services.AddSingleton` methods. I had order issues because I created the services and added them by reference. I update my question then (1 less drawback of Method A)! But the point is still made. – neggenbe Oct 13 '21 at 13:24
  • 2
    This is what the debate over "service locator" vs "dependency injection" is all about. A is dependency injection, B is service locator. See https://stackoverflow.com/questions/1557781/whats-the-difference-between-the-dependency-injection-and-service-locator-patte – Kirk Woll Oct 13 '21 at 13:52
  • Does this answer your question? [What's the difference between the Dependency Injection and Service Locator patterns?](https://stackoverflow.com/questions/1557781/whats-the-difference-between-the-dependency-injection-and-service-locator-patte) – Kirk Woll Oct 13 '21 at 13:53
  • It gives a name to it indeed. But still not clear why B is not preferred to A (here, we actually DO both DI and service locator)..?! – neggenbe Oct 13 '21 at 14:01
  • 1
    @neggenbe B is using DI to inject a service locator. The [third](https://stackoverflow.com/a/1557810/189950) answer of that question elaborates on what my reasons would be to prefer A -- dependencies are explicit in the constructor declaration instead of potentially existing anywhere in the implementation. – Kirk Woll Oct 13 '21 at 19:36
  • Ok, so A means "some kind of dependency check at compilation". Yet still nothing guarantees the service IS actually created and registered when calling the constructor. The one "good" thing is checked automatically when the service is created at runtime. So to make B "as good as" A from that perspective, I'd have to check the services I intend to use in the constructor... Correct? – neggenbe Oct 14 '21 at 19:13
  • I don't understand the argument of "adding services on the fly". You still need to update the code and compile. It is not like changing the CSS values. You either change the consuming code or add a parameter to the constructor. It is better to weed out the architectural issues related to service lifetimes in one location (startup) than at 100s of places where the service is consumed. It ensures overall better code discipline. – Mayur Ekbote Oct 16 '21 at 07:07

0 Answers0