Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net mvc 是否使用自定义ASP.NET MVC IValueProvider,而不全局设置?_Asp.net Mvc_Model Binding_Modelbinders_Value Provider - Fatal编程技术网

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

Asp.net mvc 是否使用自定义ASP.NET MVC IValueProvider,而不全局设置?,asp.net-mvc,model-binding,modelbinders,value-provider,Asp.net Mvc,Model Binding,Modelbinders,Value Provider,我希望能够从cookie中获取键/值,并使用它们绑定模型 与构建自定义ModelBinder不同,我认为DefaultModelBinder开箱即用,选择值来源的最佳方法是设置它所使用的IValueProvider 为此,我不想创建自定义ValueProviderFactory并全局绑定它,因为我只希望在特定的操作方法中使用此ValueProvider 我已经构建了一个属性来实现这一点: /// <summary> /// Replaces the current value pro

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

与构建自定义ModelBinder不同,我认为DefaultModelBinder开箱即用,选择值来源的最佳方法是设置它所使用的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);
    }
}
//
///用指定的值提供程序替换当前值提供程序
/// 
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,Inherited=true,AllowMultiple=true)]
公共类SetValueProviderAttribute:ActionFilterAttribute
{
public SetValueProviderAttribute(类型valueProviderType)
{
if(valueProviderType.GetInterface(typeof(IValueProvider.Name)==null)
抛出新ArgumentException(“类型”+valueProviderType+“必须实现接口IValueProvider。”,“valueProviderType”);
_ValueProviderType=ValueProviderType;
}
私有类型_ValueProviderType;
公共覆盖无效OnActionExecuting(ActionExecutingContext filterContext)
{
IValueProvider valueProviderToAdd=GetValueProviderToAdd();
filterContext.Controller.ValueProvider=valueProviderToAdd;
}
专用IValueProvider GetValueProviderToAdd()的
{
return(IValueProvider)Activator.CreateInstance(_ValueProviderType);
}
}

不幸的是,ModelBinder及其IValueProvider是在OnAction执行之前设置的(为什么?)。是否有其他人想出了一种方法,可以在不使用ValueProviderFactory的情况下将自定义IValueProvider注入DefaultModelBinder?

在这种情况下,您仍然应该使用
ValueProviderFactory

必须在
ValueProviderFactory
上实现的方法具有以下签名:

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

如果您不想利用cookies来处理请求,只需返回
null
,框架就会从值提供者列表中筛选出该值

另外,您可能不想将何时使用
CustomCookieValueProvider
的逻辑硬编码到
ValueProviderFactory
中。您也许可以利用
DataTokens
来匹配何时对给定路由使用cookie。因此,添加一条如下所示的路线:

routes.MapRoute("SomeRoute","{controller}/{action}").DataTokens.Add("UseCookies", true);
if (controllerContext.RouteData.DataTokens.ContainsKey("UseCookies"))
{
    return new CustomCookieValueProvider(controllerContext.RequestContext.HttpContext.Request.Cookies);
}

return null;
注意这里的
DataTokens.Add()
调用,现在在
GetValueProvider
方法中,您可以执行如下操作:

routes.MapRoute("SomeRoute","{controller}/{action}").DataTokens.Add("UseCookies", true);
if (controllerContext.RouteData.DataTokens.ContainsKey("UseCookies"))
{
    return new CustomCookieValueProvider(controllerContext.RequestContext.HttpContext.Request.Cookies);
}

return null;

我知道怎么做了。首先,创建一个自定义模型绑定器,该绑定器在构造函数中采用值提供程序类型,但从默认的modelbinder继承。这允许您将标准模型绑定与自定义值提供程序一起使用:

/// <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);
    }
}
//
///使用默认模型绑定,但设置它使用的值提供程序
/// 
公共类SetValueProviderDefaultModelBinder:DefaultModelBinder
{
私有类型_ValueProviderType;
public SetValueProviderDefaultModelBinder(类型valueProviderType)
{
if(valueProviderType.GetInterface(typeof(IValueProvider.Name)==null)
抛出新ArgumentException(“类型”+valueProviderType+“必须实现接口IValueProvider。”,“valueProviderType”);
_ValueProviderType=ValueProviderType;
}
/// 
///绑定模型之前,请设置它使用的IValueProvider
/// 
公共重写对象BindModel(ControllerContext ControllerContext,ModelBindingContext bindingContext)
{
bindingContext.ValueProvider=GetValueProvider();
返回base.BindModel(controllerContext、bindingContext);
}
私有IValueProvider GetValueProvider()
{
return(IValueProvider)Activator.CreateInstance(_ValueProviderType);
}
}
然后,我们创建一个模型绑定属性,该属性将在上面创建的自定义模型绑定器中注入值提供程序类型,并将其用作模型绑定器:

/// <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;
    }
}
//
///在默认模型绑定器上,用指定的值提供程序替换当前值提供程序。无法将自定义模型绑定器与此一起使用。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Struct,AllowMultiple=false,Inherited=false)]
公共类SetValueProviderAttribute:CustomModelBinderAttribute
{
//最初,这是一个操作过滤器,OnActionExecuting设置控制器的IValueProvider,期望它被默认的模型绑定器拾取
//绑定模型时。不幸的是,OnActionExecuting发生在DefaultModelBinder上设置了IValueProvider之后。唯一的解决方法是
//要创建从DefaultModelBinder继承的自定义模型绑定器,请在其BindModel方法中设置ValueProvider,然后执行标准模型绑定。
public SetValueProviderAttribute(类型valueProviderType)
{
if(valueProviderType.GetInterface(typeof(IValueProvider.Name)==null)
抛出新ArgumentException(“类型”+valueProviderType+“必须实现接口IValueProvider。”,“valueProviderType”);
_ValueProviderType=ValueProviderType;
}
私有类型_ValueProviderType;
公共覆盖IModelBinder GetBinder()
{
var modelBinder=新的SetValueProviderDefaultModelBinder(_ValueProviderType);
归还活页夹;
}
}

这里有一个替代方案,允许您根据actions参数将IValueProviders指定为属性。 这使得IValueProviders是暂时的,而不是全局的

public interface IControllerContextAware
{
    ControllerContext ControllerContext { get; set; }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class ValueProviderAttribute : CustomModelBinderAttribute
{
    public Type[] ValueProviders { get; private set; }

    public ValueProviderAttribute(params Type[] valueProviders)
    {
        if (valueProviders == null)
        {
            throw new ArgumentNullException("valueProviders");
        }
        foreach (var valueProvider in valueProviders.Where(valueProvider => !typeof(IValueProvider).IsAssignableFrom(valueProvider)))
        {
            throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "The valueProvider {0} must be of type {1}", valueProvider.FullName, typeof(IValueProvider)), "valueProviders");
        }

        ValueProviders = valueProviders;
    }

    public override IModelBinder GetBinder()
    {
        return new ValueProviderModelBinder
            {
                ValueProviderTypes = ValueProviders.ToList(),
                CreateValueProvider = OnCreateValueProvider
            };
    }

    protected virtual IValueProvider OnCreateValueProvider(Type valueProviderType, ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueProvider = (IValueProvider)Activator.CreateInstance(valueProviderType);
        if (valueProvider is IControllerContextAware)
        {
            (valueProvider as IControllerContextAware).ControllerContext = controllerContext;
        }
        return valueProvider;
    }

    private class ValueProviderModelBinder : DefaultModelBinder
    {
        public IList<Type> ValueProviderTypes { get; set; }
        public Func<Type, ControllerContext, ModelBindingContext, IValueProvider> CreateValueProvider { get; set; }

        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var valueProviders = from type in ValueProviderTypes
                                 select CreateValueProvider(type, controllerContext, bindingContext);

            bindingContext.ValueProvider = new ValueProviderCollection(valueProviders.Concat((Collection<IValueProvider>)bindingContext.ValueProvider).ToList());

            return base.BindModel(controllerContext, bindingContext);
        }
    }
}
然后我们有了Ivalued
    [HttpGet]
    public ActionResult Test()
    {
        return View(new SomeModel());
    }

    [HttpPost]
    public ActionResult Test([ValueProvider(typeof(MyValueProvider))]SomeModel model)
    {
        return View(model);
    }