1

Supposing I have a controller class that takes an instance of a service class as an argument in its constructor, which is injected via dependency injection:

    [ApiController]
    [Route("api/[controller]")]
    public class StudentsController : ControllerBase
    {
        private readonly IStudentsService _studentsService;

        public StudentsController(IStudentsService studentsService)
        {
            _studentsService= studentsService;
        }

And supposing that the injected service takes an instance of Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary as an argument in the constructor, like this:

    public class StudentsService : IStudentsService
    {
        private ModelStateDictionary _modelStateDictionary;

        public StudentsService(ModelStateDictionary modelStateDictionary)
        {
            _modelStateDictionary = modelStateDictionary;
        }

Is there a way that I can ensure that the ModelStateDictionary injected into the constructor of the StudentsService isn't just any ModelStateDictionary but is the ModelStateDictionary being used by the particular StudentsController that instantiated the StudentsService? I.e. the controller that is associated with the current request to my API?

Perhaps it might look something like this in Startup:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddScoped<IStudentsService, StudentsService>(provider => provider.DoSomethingCleverToEnsureThisServiceGetsTheModelStateDictionaryOfItsController()
            );

The reason I wish to do this is so that I can perform further validation on the request in the service tier and pass validation errors back the controller using its own ModelStateDictionary. Thanks for any advice!

4
  • Please check stackoverflow.com/questions/49374002/… Commented Feb 7, 2021 at 10:02
  • 2
    A word of advice: don't make your service depend on ASP.NET internal logic Commented Feb 7, 2021 at 10:09
  • Hi @Nayan, I read the link but I can't understand how it will solve the problem. Could you elaborate please or maybe I just haven't explained it well? Commented Feb 7, 2021 at 11:27
  • Hi @Xerillio, this link was very interesting thank you and makes a lot of sense. I had a feeling I might be doing something a bit wrong. I was just following this tutorial here: learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/…. I could sense there was something a bit funny about it (like saying it was decoupling but it never quite decoupled). The answer you shared by Tseng seems to be closer to what I should be doing. Commented Feb 7, 2021 at 11:29

1 Answer 1

1

After reading the StackOverflow question offered by @Xerillio, I learned two things:

  1. The direct answer to my question is to use the IActionContextAccessor interface in Microsoft.AspNetCore.Mvc.Infrastructure injecting it into the service, thus providing a reference to the controller and its ModelState:
public class StudentService : IStudentService
{
    private readonly ModelStateDictionary _modelState;

    public MyService(IActionContextAccessor actionContextAccessor)
    {
        _modelState = actionContextAccessor.Context.ModelState;
    }
  1. But despite getting this direct answer, it still might not be a sensible thing to do. This tutorial is what I originally used:

https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/models-data/validating-with-a-service-layer-cs

But it is quite old now, and it never delivered on its promise to fully decouple the services from the controllers. A better way to achieve what I want might be to follow one of the three solutions proposed by @Tseng, here:

Injecting ModelState into Service

It argues quite convincingly why it would be better not to couple the service layer to ASP .NET MVC, so it can be used across desktop and console applications (and beyond).

And that is what I shall now do instead of persevering with the Microsoft tutorial.

I hope that helps other people with this question.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.