如何在 ASP.NET Core 中使用支持依赖注入的自定义模型绑定器?
How do I use custom model binder that supports dependency injection in ASP.NET Core?
我正在尝试在 MVC 中使用自定义模型绑定器,我想从我的 IoC 容器中解析它。我遇到的问题是我在添加 MVC 服务时无法访问我的容器,因为我的容器尚未构建(我需要在构建我的容器之前添加 MVC)。感觉像是一个 chicken/egg 问题,我确定我缺少一个简单的解决方案。
示例:
services.AddMvc().AddMvcOptions(options =>
{
options.ModelBinders.Add(serviceProvider.Resolve<CustomModelBinder>());
});
我的自定义模型活页夹如下所示:
public class CustomModelBinder : IModelBinder
{
private IServiceProvider serviceProvider;
public CustomModelBinder(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
{
var model = serviceProvider.GetService(bindingContext.ModelType);
bindingContext.Model = model;
var binder = new GenericModelBinder();
return binder.BindModelAsync(bindingContext);
}
}
根据此处的 post:https://github.com/aspnet/Mvc/issues/4167
要直接回答您的问题,请使用:
bindingContext.OperationBindingContext.ActionContext.HttpContext.RequestServices
附带说明一下,您还可以选择使用 [FromServices]
来为您解决问题。
对于 ASP.NET Core 2.2,这应该适用于 DI:
public class CustomModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
// dependency injection
var model = bindingContext.HttpContext.RequestServices.GetService(bindingContext.ModelType);
// other changes...
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
您可以使用 IConfigureOptions
通过依赖注入配置选项,而不是使用在模型绑定器中检索所需服务的服务定位器模式。这允许您延迟配置选项,直到构建依赖注入容器。
这是一个依赖于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.
}
}
您需要此模型活页夹的提供商:
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);
}
}
通常,您会使用如下代码在 Startup
中添加模型活页夹提供程序:
services.AddMvc().AddMvcOptions(options =>
{
options.ModelBinderProviders.Add(new CustomModelBinderProvider(customService));
});
但是,正如您所指出的,这是不可能的。正在配置系统时无法解析 customService
。这个问题的一般解决方案是使用 IConfigureOptions
和上面的代码来配置选项,但具有能够使用依赖注入的额外好处:
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));
}
必须将此 class 添加到 Startup
中的容器中
services.AddSingleton<IConfigureOptions<MvcOptions>, CustomModelBinderConfigureMvcOptions>();
您现在可以使用具有依赖关系的模型绑定器。
我正在尝试在 MVC 中使用自定义模型绑定器,我想从我的 IoC 容器中解析它。我遇到的问题是我在添加 MVC 服务时无法访问我的容器,因为我的容器尚未构建(我需要在构建我的容器之前添加 MVC)。感觉像是一个 chicken/egg 问题,我确定我缺少一个简单的解决方案。
示例:
services.AddMvc().AddMvcOptions(options =>
{
options.ModelBinders.Add(serviceProvider.Resolve<CustomModelBinder>());
});
我的自定义模型活页夹如下所示:
public class CustomModelBinder : IModelBinder
{
private IServiceProvider serviceProvider;
public CustomModelBinder(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
{
var model = serviceProvider.GetService(bindingContext.ModelType);
bindingContext.Model = model;
var binder = new GenericModelBinder();
return binder.BindModelAsync(bindingContext);
}
}
根据此处的 post:https://github.com/aspnet/Mvc/issues/4167
要直接回答您的问题,请使用:
bindingContext.OperationBindingContext.ActionContext.HttpContext.RequestServices
附带说明一下,您还可以选择使用 [FromServices]
来为您解决问题。
对于 ASP.NET Core 2.2,这应该适用于 DI:
public class CustomModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
// dependency injection
var model = bindingContext.HttpContext.RequestServices.GetService(bindingContext.ModelType);
// other changes...
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
您可以使用 IConfigureOptions
通过依赖注入配置选项,而不是使用在模型绑定器中检索所需服务的服务定位器模式。这允许您延迟配置选项,直到构建依赖注入容器。
这是一个依赖于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.
}
}
您需要此模型活页夹的提供商:
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);
}
}
通常,您会使用如下代码在 Startup
中添加模型活页夹提供程序:
services.AddMvc().AddMvcOptions(options =>
{
options.ModelBinderProviders.Add(new CustomModelBinderProvider(customService));
});
但是,正如您所指出的,这是不可能的。正在配置系统时无法解析 customService
。这个问题的一般解决方案是使用 IConfigureOptions
和上面的代码来配置选项,但具有能够使用依赖注入的额外好处:
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));
}
必须将此 class 添加到 Startup
services.AddSingleton<IConfigureOptions<MvcOptions>, CustomModelBinderConfigureMvcOptions>();
您现在可以使用具有依赖关系的模型绑定器。