When to Use The FromService Attribute

I recently discovered the [FromServices] attribute, which has been a part of .Net Core since the first version.

The [FromServices] attribute allows method level dependency injection in Asp.Net Core controllers.

Here’s an example:

public class UserController : Controller
{
    private readonly IApplicationSettings _applicationSettings;

    public UserController(IApplicationSettings applicationSettings)
    {
        _applicationSettings = applicationSettings;
    }

    public IActionResult Get([FromService]IUserRepository userRepository, int userId)
    {
        //Do magic
    }
}

Why use method injection over constructor injection? The common explanation is when a method needs dependencies and it’s not used anywhere else, then it’s a candidate for using the [FromService] attribute.

Steven from StackOverflow posted an answer against using the [FromService] attribute:

For me, the use of this type of method injection into controller actions is a bad idea, because:

– Such [FromServices] attribute can be easily forgotten, and you will only find out when the action is invoked (instead of finding out at application start-up, where you can verify the application’s configuration)

– The need for moving away from constructor injection for performance reasons is a clear indication that injected components are too heavy to create, while injection constructors should be simple, and component creation should, therefore, be very lightweight.

– The need for moving away from constructor injection to prevent constructors from becoming too large is an indication that your classes have too many dependencies and are becoming too complex. In other words, having many dependencies is an indication that the class violates the Single Responsibility Principle. The fact that your controller actions can easily be split over different classes is proof that such controller is not very cohesive and, therefore, an indication of a SRP violation.

So instead of hiding the root problem with the use of method injection, I advise the use of constructor injection as sole injection pattern here and make your controllers smaller. This might mean, however, that your routing scheme becomes different from your class structure, but this is perfectly fine, and completely supported by ASP.NET Core.

From a testability perspective, btw, it shouldn’t really matter if there sometimes is a dependency that isn’t needed. There are effective test patterns that fix this problem.

I agree with Steven; if you need to move your dependencies from your controller to the method because the class is constructing too many dependencies, then it’s time to break up the controller. You’re almost certainly violating SRP.

The only use case I see with method injection is late-binding when a dependency that isn’t ready at controller construction. Otherwise, it’s better to use constructor injection.

I say this because with constructor injection the class knows at construction whether the dependencies are available. With method injection, this isn’t the case, it’s not known if the dependencies are available until the method is called.

1 Comment

  1. I really like programming patterns that produce code smell when appropriate. that `[FromServices]` might _hide_ code smell is, for me, a compelling argument against using it.

    I would like to offer a couple upsides that I see to `[FromServices]`:
    * a performance bug or other issue in a service which one method depends on won’t affect every other method in the same class. (our services _should_ be small and light, but especially when working with other devs on a long-running project, there’s no guarantee they always will be, at all times.)
    * it makes the controller method more portable/easier to refactor, since that method has no dependencies on the class it is in. easier to refactor means a dev is more likely to “casually” perform that refactor (because they happened to notice, even if it wasn’t explicitly part of the issue they were addressing), maintaining SRP organically.

Leave a Reply to Ben Cancel reply

Your email address will not be published. Required fields are marked *