TL;DR:当两个自定义 ModelBinder
实现都依赖 Autofac 来注入(inject)(通用)时,如何将两个自定义 ModelBinder 实现共享的逻辑合并到单个基类中对他们的依赖?
在查看我正在处理的 ASP.NET MVC 项目中的一些代码时,我意识到我有两个自定义模型绑定(bind)程序,它们本质上执行相同的操作。它们都继承自 DefaultModelBinder
,并且都使用注入(inject)到其构造函数中的 IEncodingService
对两个单独的 View 模型类上的单个属性进行编码。
public class ResetQuestionAndAnswerViewModelBinder : DefaultModelBinder {
public ResetQuestionAndAnswerViewModelBinder(IEncodingService encodingService) {
encoder = encodingService;
}
private readonly IEncodingService encoder;
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext) {
var model = base.BindModel(controllerContext, bindingContext) as ResetQuestionAndAnswerViewModel;
if (model != null) {
var answer = bindingContext.ValueProvider.GetValue("Answer");
if ((answer != null) && !(answer.AttemptedValue.IsNullOrEmpty())) {
model.Answer = encoder.Encode(answer.AttemptedValue);
}
}
return model;
}
}
public class ConfirmIdentityViewModelBinder : DefaultModelBinder {
public ConfirmIdentityViewModelBinder(IEncodingService encodingService) {
encoder = encodingService;
}
private readonly IEncodingService encoder;
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext) {
var model = base.BindModel(controllerContext, bindingContext) as ConfirmIdentityViewModel;
if (model != null) {
var secretKey = bindingContext.ValueProvider.GetValue("SecretKey");
if ((secretKey != null) && !(secretKey.AttemptedValue.IsNullOrEmpty())) {
model.SecretKeyHash = encoder.Encode(secretKey.AttemptedValue);
}
}
return model;
}
}
我为这两个类编写了一个通用基类来继承:
public class EncodedPropertyModelBinder<TViewModel> : DefaultModelBinder
where TViewModel : class {
public EncodedPropertyModelBinder(IEncodingService encodingService,
string propertyName) {
encoder = encodingService;
property = propertyName;
}
private readonly IEncodingService encoder;
private readonly string property;
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext) {
var model = base.BindModel(controllerContext, bindingContext) as TViewModel;
if (model != null) {
var value = bindingContext.ValueProvider.GetValue(property);
if ((value != null) && !(value.AttemptedValue.IsNullOrEmpty())) {
var encodedValue = encoder.Encode(value.AttemptedValue);
var propertyInfo = model.GetType().GetProperty(property);
propertyInfo.SetValue(model, encodedValue, null);
}
}
return model;
}
}
使用 Autofac,如何将 IEncodingService
注入(inject)基类构造函数,同时强制派生类提供要编码的属性名称?
最佳答案
实际上,我的处理方法略有不同,favoring composition over inheritance 。这意味着我将封装属性操作的细节,并将不同的实现传递给单个模型绑定(bind)器。
首先,定义一个表示绑定(bind)单个属性的接口(interface):
public interface IPropertyBinder
{
void SetPropertyValue(object model, ModelBindingContext context);
}
然后,使用最初来自 EncodedPropertyModelBinder
的参数来实现它:
public sealed class PropertyBinder : IPropertyBinder
{
private readonly IEncodingService _encodingService;
private readonly string _propertyName;
public PropertyBinder(IEncodingService encodingService, string propertyName)
{
_encodingService = encodingService;
_propertyName = propertyName;
}
public void SetPropertyValue(object model, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(_propertyName);
if(value != null && !value.AttemptedValue.IsNullOrEmpty())
{
var encodedValue = _encodingService.Encode(value.AttemptedValue);
var property = model.GetType().GetProperty(_propertyName);
property.SetValue(model, encodedValue, null);
}
}
}
接下来,使用新接口(interface)实现EncodedPropertyModelBinder
:
public class EncodedPropertyModelBinder : DefaultModelBinder
{
private readonly IPropertyBinder _propertyBinder;
public EncodedPropertyModelBinder(IPropertyBinder propertyBinder)
{
_propertyBinder = propertyBinder;
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = base.BindModel(controllerContext, bindingContext);
if(model != null)
{
_propertyBinder.SetPropertyValue(model, bindingContext);
}
return model;
}
}
最后,使用 Autofac 命名实例注册 View 模型的两个版本,传入 PropertyBinder
的不同配置:
builder.
Register(c => new EncodedPropertyModelBinder(new PropertyBinder(c.Resolve<IEncodingService>(), "Answer")))
.Named<EncodedPropertyModelBinder>("AnswerBinder");
builder.
Register(c => new EncodedPropertyModelBinder(new PropertyBinder(c.Resolve<IEncodingService>(), "SecretKey")))
.Named<EncodedPropertyModelBinder>("SecretKeyBinder");
关于asp.net-mvc - 使用 Autofac 将服务注入(inject)基类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4088716/