2010-11-03 173 views
2

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到基類的構造函數,同時迫使派生類提供要編碼的屬性的名稱?

回答

3

我實際上會略有不同,通過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");