使用自定义ASP.NET MVC IValueProvider,而不是全局设置?

Ove*_*d D 12 asp.net-mvc model-binding modelbinders value-provider

我希望能够从cookie中获取键/值并使用它来绑定模型.

我认为DefaultModelBinder不是构建自定义的ModelBinder,而是开箱即用,选择值来自哪里的最佳方法是设置它使用的IValueProvider.

为此,我不想创建自定义ValueProviderFactory并将其全局绑定,因为我只希望在特定的操作方法中使用此ValueProvider.

我已经构建了一个执行此操作的属性:

/// <summary>
/// Replaces the current value provider with the specified value provider
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class SetValueProviderAttribute : ActionFilterAttribute
{
    public SetValueProviderAttribute(Type valueProviderType)
    {
        if (valueProviderType.GetInterface(typeof(IValueProvider).Name) == null)
            throw new ArgumentException("Type " + valueProviderType + " must implement interface IValueProvider.", "valueProviderType");

        _ValueProviderType = valueProviderType;
    }

    private Type _ValueProviderType;

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        IValueProvider valueProviderToAdd = GetValueProviderToAdd();

        filterContext.Controller.ValueProvider = valueProviderToAdd;
    }

    private IValueProvider GetValueProviderToAdd()
    {
        return (IValueProvider)Activator.CreateInstance(_ValueProviderType);
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,ModelBinder及其IValueProvider是在OnActionExecuting之前设置的(为什么?????).有没有其他人想出一种方法将自定义IValueProvider注入DefaultModelBinder而不使用ValueProviderFactory?

nik*_*d23 10

ValueProviderFactory在这种情况下你仍然应该使用a .

您必须在您的方法上实施ValueProviderFactory此方法的签名:

IValueProvider GetValueProvider(ControllerContext controllerContext)
Run Code Online (Sandbox Code Playgroud)

在您的方法实现中,您可以检查控制器上下文,如果传入请求是针对您要利用cookie的控制器/操作,则返回一些CustomCookieValueProvider.

如果您不想为请求利用cookie,只需返回null,框架将从Value Provider列表中过滤掉.

作为奖励,你可能不想硬代码时使用的逻辑CustomCookieValueProviderValueProviderFactory.或许,您可以利用DataTokens匹配何时使用给定路线的Cookie.所以添加这样的路线:

routes.MapRoute("SomeRoute","{controller}/{action}").DataTokens.Add("UseCookies", true);
Run Code Online (Sandbox Code Playgroud)

注意DataTokens.Add()那里的调用,现在在你的GetValueProvider方法中,你可以这样做:

if (controllerContext.RouteData.DataTokens.ContainsKey("UseCookies"))
{
    return new CustomCookieValueProvider(controllerContext.RequestContext.HttpContext.Request.Cookies);
}

return null;
Run Code Online (Sandbox Code Playgroud)


Ove*_*d D 3

弄清楚如何做到这一点。首先,创建一个自定义模型绑定程序,该模型绑定程序在构造函数中采用值提供程序类型 - 但继承自默认模型绑定程序。这允许您将标准模型绑定与自定义值提供程序一起使用:

/// <summary>
/// Uses default model binding, but sets the value provider it uses
/// </summary>
public class SetValueProviderDefaultModelBinder : DefaultModelBinder
{
    private Type _ValueProviderType;

    public SetValueProviderDefaultModelBinder(Type valueProviderType)
    {
        if (valueProviderType.GetInterface(typeof(IValueProvider).Name) == null)
            throw new ArgumentException("Type " + valueProviderType + " must implement interface IValueProvider.", "valueProviderType");

        _ValueProviderType = valueProviderType;
    }

    /// <summary>
    /// Before binding the model, set the IValueProvider it uses
    /// </summary>
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        bindingContext.ValueProvider = GetValueProvider();

        return base.BindModel(controllerContext, bindingContext);
    }

    private IValueProvider GetValueProvider()
    {
        return (IValueProvider)Activator.CreateInstance(_ValueProviderType);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我们创建一个模型绑定属性,它将在上面创建的自定义模型绑定程序中注入值提供程序类型,并将其用作模型绑定程序:

/// <summary>
/// On the default model binder, replaces the current value provider with the specified value provider.  Cannot use custom model binder with this.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
public class SetValueProviderAttribute : CustomModelBinderAttribute
{
    // Originally, this was an action filter, that OnActionExecuting, set the controller's IValueProvider, expecting it to be picked up by the default model binder
    // when binding the model.  Unfortunately, OnActionExecuting occurs AFTER the IValueProvider is set on the DefaultModelBinder.  The only way around this is
    // to create a custom model binder that inherits from DefaultModelBinder, and in its BindModel method set the ValueProvider and then do the standard model binding.

    public SetValueProviderAttribute(Type valueProviderType)
    {
        if (valueProviderType.GetInterface(typeof(IValueProvider).Name) == null)
            throw new ArgumentException("Type " + valueProviderType + " must implement interface IValueProvider.", "valueProviderType");

        _ValueProviderType = valueProviderType;
    }

    private Type _ValueProviderType;

    public override IModelBinder GetBinder()
    {
        var modelBinder = new SetValueProviderDefaultModelBinder(_ValueProviderType);
        return modelBinder;
    }
}
Run Code Online (Sandbox Code Playgroud)