注入服务为使用Autofac

问题描述:

TL基类; DR:如何可以巩固通过两个自定义ModelBinder实现共享成一个单一的基类的逻辑,当两个实现依赖于Autofac注入一个(共同的)的依赖性放进去?注入服务为使用Autofac


在审查一个ASP.NET MVC项目我工作的一些代码,我意识到,我有两个自定义模型粘合剂,基本上他们做同样的事情。它们都从DefaultModelBinder继承,它们都使用注入到其构造函数中的IEncodingService对两个单独的视图模型类编码单个属性。

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到基类的构造函数,同时迫使派生类提供要编码的属性的名称?

我实际上会略有不同,通过favoring composition over inheritance。这意味着我将封装属性操作的细节,并将不同的实现传递给单个模型绑定器。

首先,定义其表示结合单个属性的接口:

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); 
     } 
    } 
} 

接着,实施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命名实例注册两个版本的视图模型,传入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");