MVC扩展(ModelBinder)

1.从最简单的开始

model

public class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
}

cshtml

@using (Html.BeginForm())
{
            <div>@Html.TextBoxFor(_ => _.ID)</div> 
        <div>@Html.TextBoxFor(_ => _.Name)</div> 
        <input type="submit" value="Submit" />
    }

 

controller

public ActionResult Person()
    {
        var person = new Person() {ID = "PsersonId", Name = "PersonName"};
        return View();
    }

    [HttpPost]
    public ActionResult Person(Person person)
    {
        return View();
    }

Person和表单元素实现了自动绑定。省去了逐一赋值的步骤。当页面的字段增多时,ModelBind机制可以节省不少开发时间。

2.实现原理

绑定的过程由MVC中的DefaultModelBinder完成。

DefaultModelBinder签名如下

/// <summary>
  /// Maps a browser request to a data object. This class provides a concrete implementation of a model binder.
  /// </summary>
  public class DefaultModelBinder : IModelBinder
  {
    /// <summary>
    /// Binds the model by using the specified controller context and binding context.
    /// </summary>
    /// 
    /// <returns>
    /// The bound object.
    /// </returns>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param><exception cref="T:System.ArgumentNullException">The <paramref name="bindingContext "/>parameter is null.</exception>
    public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext);
    /// <summary>
    /// Binds the specified property by using the specified controller context and binding context and the specified property descriptor.
    /// </summary>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param><param name="propertyDescriptor">Describes a property to be bound. The descriptor provides information such as the component type, property type, and property value. It also provides methods to get or set the property value.</param>
    protected virtual void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor);
    /// <summary>
    /// Creates the specified model type by using the specified controller context and binding context.
    /// </summary>
    /// 
    /// <returns>
    /// A data object of the specified type.
    /// </returns>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param><param name="modelType">The type of the model object to return.</param>
    protected virtual object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType);
    /// <summary>
    /// Creates an index (a subindex) based on a category of components that make up a larger index, where the specified index value is an integer.
    /// </summary>
    /// 
    /// <returns>
    /// The name of the subindex.
    /// </returns>
    /// <param name="prefix">The prefix for the subindex.</param><param name="index">The index value.</param>
    protected static string CreateSubIndexName(string prefix, int index);
    /// <summary>
    /// Creates an index (a subindex) based on a category of components that make up a larger index, where the specified index value is a string.
    /// </summary>
    /// 
    /// <returns>
    /// The name of the subindex.
    /// </returns>
    /// <param name="prefix">The prefix for the subindex.</param><param name="index">The index value.</param>
    protected static string CreateSubIndexName(string prefix, string index);
    /// <summary>
    /// Creates the name of the subproperty by using the specified prefix and property name.
    /// </summary>
    /// 
    /// <returns>
    /// The name of the subproperty.
    /// </returns>
    /// <param name="prefix">The prefix for the subproperty.</param><param name="propertyName">The name of the property.</param>
    protected internal static string CreateSubPropertyName(string prefix, string propertyName);
    /// <summary>
    /// Returns a set of properties that match the property filter restrictions that are established by the specified <paramref name="binding context"/>.
    /// </summary>
    /// 
    /// <returns>
    /// An enumerable set of property descriptors.
    /// </returns>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param>
    protected IEnumerable<PropertyDescriptor> GetFilteredModelProperties(ControllerContext controllerContext, ModelBindingContext bindingContext);
    /// <summary>
    /// Returns the properties of the model by using the specified controller context and binding context.
    /// </summary>
    /// 
    /// <returns>
    /// A collection of property descriptors.
    /// </returns>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param>
    protected virtual PropertyDescriptorCollection GetModelProperties(ControllerContext controllerContext, ModelBindingContext bindingContext);
    /// <summary>
    /// Returns the value of a property using the specified controller context, binding context, property descriptor, and property binder.
    /// </summary>
    /// 
    /// <returns>
    /// An object that represents the property value.
    /// </returns>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param><param name="propertyDescriptor">The descriptor for the property to access. The descriptor provides information such as the component type, property type, and property value. It also provides methods to get or set the property value.</param><param name="propertyBinder">An object that provides a way to bind the property.</param>
    protected virtual object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder);
    /// <summary>
    /// Returns the descriptor object for a type that is specified by its controller context and binding context.
    /// </summary>
    /// 
    /// <returns>
    /// A custom type descriptor object.
    /// </returns>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param>
    protected virtual ICustomTypeDescriptor GetTypeDescriptor(ControllerContext controllerContext, ModelBindingContext bindingContext);
    /// <summary>
    /// Determines whether a data model is valid for the specified binding context.
    /// </summary>
    /// 
    /// <returns>
    /// true if the model is valid; otherwise, false.
    /// </returns>
    /// <param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param><exception cref="T:System.ArgumentNullException">The <paramref name="bindingContext"/> parameter is null.</exception>
    protected static bool IsModelValid(ModelBindingContext bindingContext);
    /// <summary>
    /// Called when the model is updated.
    /// </summary>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param>
    protected virtual void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext);
    /// <summary>
    /// Called when the model is updating.
    /// </summary>
    /// 
    /// <returns>
    /// true if the model is updating; otherwise, false.
    /// </returns>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param>
    protected virtual bool OnModelUpdating(ControllerContext controllerContext, ModelBindingContext bindingContext);
    /// <summary>
    /// Called when the specified property is validated.
    /// </summary>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param><param name="propertyDescriptor">Describes a property to be validated. The descriptor provides information such as the component type, property type, and property value. It also provides methods to get or set the property value.</param><param name="value">The value to set for the property.</param>
    protected virtual void OnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value);
    /// <summary>
    /// Called when the specified property is validating.
    /// </summary>
    /// 
    /// <returns>
    /// true if the property is validating; otherwise, false.
    /// </returns>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param><param name="propertyDescriptor">Describes a property being validated. The descriptor provides information such as component type, property type, and property value. It also provides methods to get or set the property value.</param><param name="value">The value to set for the property.</param>
    protected virtual bool OnPropertyValidating(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value);
    /// <summary>
    /// Sets the specified property by using the specified controller context, binding context, and property value.
    /// </summary>
    /// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param><param name="propertyDescriptor">Describes a property to be set. The descriptor provides information such as the component type, property type, and property value. It also provides methods to get or set the property value.</param><param name="value">The value to set for the property.</param>
    protected virtual void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value);
    /// <summary>
    /// Gets or sets the model binders for the application.
    /// </summary>
    /// 
    /// <returns>
    /// The model binders for the application.
    /// </returns>
    protected internal ModelBinderDictionary Binders { get; set; }
    /// <summary>
    /// Gets or sets the name of the resource file (class key) that contains localized string values.
    /// </summary>
    /// 
    /// <returns>
    /// The name of the resource file (class key).
    /// </returns>
    public static string ResourceClassKey { get; set; }
  }

关于DefaultModelBinder的具体实现分析。 请参考http://www.cnblogs.com/artech/archive/2012/05/21/model-binder-provision.html

DefaultModelBinder继承自IModelBinder. IModelBinder的接口

public interface IModelBinder {
        object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext);
    }

DefaultModelBinder继承自IModelBinder同时DefaultModelBinder中有许多virtual方法我们可以直接实现IModelBinder或者DefaultModelBinder并重写相应的虚方法实现这两种方式自定义的ModelBinder。

 

 

3自定义ModelBinder

3.1.实现IModelBinder

 

Model

public class Settings
{
    public string ServerName { get; set; }
    public string Ip { get; set; }
    public string PortNumber { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public RequestInfo RequestInfo { get; set; }
}

public  class  RequestInfo
{
    public string Url { get; set; }
    public string ClientIP { get; set; }
}

Web.config

<appSettings>

    <add key="ServerName" value="Ruby"/>
    <add key="Ip" value="192.168.0.1"/>
    <add key="PortNumber" value="21"/>
    <add key="Username" value="admin"/>
    <add key="Password" value="password"/>
  </appSettings>

SettingsBinder

public class SettingsBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var request = controllerContext.HttpContext.Request;
        var setting = new Settings()
                          {
                              Ip = ConfigurationManager.AppSettings["Ip"],
                              ServerName = ConfigurationManager.AppSettings["ServerName"],
                              PortNumber = ConfigurationManager.AppSettings["PortNumber"],
                              Username = ConfigurationManager.AppSettings["Username"],
                              Password = ConfigurationManager.AppSettings["Password"],
                              RequestInfo = new RequestInfo()
                                                {
                                                    Url = request.Url.ToString(),
                                                    ClientIP = request.ServerVariables["REMOTE_ADDR"]
                                                }
                          };
        return setting;
    }
}

Controller

[HttpPost]
    public ActionResult Person2([ModelBinder(typeof(SettingsBinder))]Settings settings)
    {
    }

这时settings已经读取了Web.config和Request的值。不必在Controller中赋值。这是一个较简单的例子,实际应用当中往往还需要各种环境参数。BindModel方法的参数controllerContext和bindingContext已经包含了大多常用的上下文数据。如HttpContext,ControllerContext,ModelType,ModelMetadata等.

 

3.2 实现DefaultModelBinder

遇到稍微复杂的模型绑定,IModelBinder就显得力不从心了。这时就需要更加强大的DefaultModelBinder来帮忙了。

需求:Trim模型中的字符串。上代码

定义TrimAttribute

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class TrimAttribute : Attribute
{
    public TrimAttribute()
    {
    }

}

 

Model

[ModelBinder(typeof(StringTrimModelBinder))]
public class Person
{
    [Required]
    [Trim]
    public string ID { get; set; }
    public string Name { get; set; }
}

StringTrimModelBinder

public class StringTrimModelBinder : DefaultModelBinder
{
    protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
    {
        var obj = base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
        if (obj is string
            && propertyDescriptor.Attributes[typeof(TrimAttribute)] != null)
        {
            return (obj as string).Trim();
        }
        return obj;
    }
}

StringTrimModelBinder继承自DefaultModelBinder。。查看DefaultModelBinder的虚方法,发现正好有GetPropertyValue这个方法,断重写之。判断是String且有[Trim]标记。

 

View

 

@using System.Web.Mvc.Html
@model Person

@{
    ViewBag.Title = "title";
}
@using (Html.BeginForm())
{
            <div>@Html.TextBoxFor(_ => _.ID)</div> 
        <div>@Html.TextBoxFor(_ => _.Name)</div> 
    <input type="submit" value="Submit" />
    @Html.ValidationSummary()
    }

运行效果

1

2

 Demo下载

参考:

http://www.cnblogs.com/artech/archive/2012/05/21/model-binder-provision.html

http://www.cnblogs.com/cjnmy36723/archive/2011/07/31/2122843.html

http://www.cnblogs.com/ldp615/archive/2010/07/30/sensitivewordsfiltermodelbinder.html

posted @ 2013-01-21 12:55  不夜橙  阅读(9005)  评论(1编辑  收藏  举报