asp.net-mvc - 使用 Autofac 将服务注入(inject)基类

标签 asp.net-mvc dependency-injection modelbinders autofac

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/

相关文章:

jquery - MVC 4 - 更新部分 View

c# - ASP.NET 4.5.2 MVC 项目模板中的身份验证系统是否适合在实际应用程序中使用?

java - 如何使用 Guice 将参数传递给 Provider?

asp.net-mvc - asp.net mvc : TryUpdateModel return value or ModelState. 是否有效?

asp.net-mvc - 更改默认值 "The {0} field is required"(最终解决方案?)

c# - 这是获取 HttpContext 请求正文的安全方法吗

asp.net-mvc - 弹出编辑模式下的多列

python - 模块的依赖注入(inject)

java - 在 Presenter 类中实例化 DI 组件是一种好习惯吗?

asp.net-mvc - IModelBinder 上的 BindProperty 和 SetProperty 有什么区别