steventnorris
steventnorris

Reputation: 5896

Dependency Injection with ModelState

First, I am aware that using ModelState in a service is typically frowned upon because it tightly couples the service to the Mvc framework. In our case, this isn't a problem, but I do eventually have plans to migrate to an IValidationDictionary and a ModelState wrapper, but need this step to work for now.

Now, on to the issue, this cool guy right here:

public class BaseService : IBaseService
    {

      protected MIRTContext _context;
      protected IMapper _mapper;
      //TODO: This tightly couples .NET MVC to our services. 
      // Could be imporoved with an interface and a ModelState wrapper
      // in order to decouple.
      private ModelStateDictionary _modelState;

      public BaseService(
        MIRTContext context, 
        IMapper mapper,
        ModelStateDictionary modelState
      ) {
        _context = context;
        _mapper = mapper;
        _modelState = modelState;
      }

     async Task<bool> IBaseService.SaveContext() {
        if(_modelState.IsValid) {
          try {
            await _context.SaveChangesAsync();
            return true;
          }
          catch {
            return false;
          }
        }
        else {
          return false;
        }
      }
    }

It keeps giving me this error:

Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary' while attempting to activate

I'm assuming I'm missing some sort of AddSingleton thing in my ConfigureServices in Startup.cs but I can't seem to figure out what. Anyone know how to get this to dependency inject properly?

Upvotes: 1

Views: 1145

Answers (1)

Kirk Larkin
Kirk Larkin

Reputation: 93203

ModelState isn't available via Dependency Injection, but you can use IActionContextAccessor, which provides access to the current ActionContext and its ModelState property.

First, you need to register IActionContextAccessor for DI:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

Next, update your BaseService class to use it:

public class BaseService : IBaseService
{
    // ...

    private readonly IActionContextAccessor _actionContextAccessor;

    public BaseService(
        // ...
        IActionContextAccessor actionContextAccessor
    ) {
        // ...
        _actionContextAccessor = actionContextAccessor;
    }

    async Task<bool> IBaseService.SaveContext() {
        var actionContext = _actionContextAccessor.ActionContext;

        if (actionContext.ModelState.IsValid) {
            // ...
        }
        else {
            return false;
        }
    }
}

Note that actionContext above will be null if the call to SaveContext is outside of MVC and its controllers, filters, etc.

Upvotes: 3

Related Questions