Instead of using a service locator pattern where the required service is retrieved in the model binder you can use IConfigureOptions
to configure options with dependency injection. This allows you to delay the configuration of options until the dependency injection container has been built.
Here is a model binder having a dependency on ICustomService
:
class CustomModelBinder : IModelBinder
{
private readonly ICustomService customService;
public CustomModelBinder(ICustomService customService) => this.customService = customService;
public Task BindModelAsync(ModelBindingContext bindingContext)
{
// Use customService during model binding.
}
}
You need a provider for this model binder:
class CustomModelBinderProvider : IModelBinderProvider
{
private readonly ICustomService customService;
public CustomModelBinderProvider(ICustomService customService) => this.customService = customService;
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
// Return CustomModelBinder or null depending on context.
return new CustomModelBinder(customService);
}
}
Normally, you would add the model binder provider in Startup
using code like this:
services.AddMvc().AddMvcOptions(options =>
{
options.ModelBinderProviders.Add(new CustomModelBinderProvider(customService));
});
However, as you have noted this is not possible. You cannot resolve customService
while the system is being configured. The general solution to this problem is to use IConfigureOptions
with the above code to configure the options but with the added benefit of being able to use dependency injection:
class CustomModelBinderConfigureMvcOptions : IConfigureOptions<MvcOptions>
{
private readonly ICustomService customService;
public CustomModelBinderConfigureMvcOptions(ICustomService customService) => this.customService = customService;
public void Configure(MvcOptions options)
=> options.ModelBinderProviders.Add(new CustomModelBinderProvider(customService));
}
This class has to be added to the container in Startup
services.AddSingleton<IConfigureOptions<MvcOptions>, CustomModelBinderConfigureMvcOptions>();
You are now able to use a model binder with a dependency.