Following this question I was wondering if there is anyway to make MVC model binder only bind elements to a list if there is a value to populate them. For example if have a form with three inputs with the same name and one value isn't entered how do I stop MVC binding a list that has 3 elements one of which is null?

有帮助吗?

解决方案

Custom Model Binder

You could implement your own model binder to prevent the null values from being added to the list:

View:

@model MvcApplication10.Models.IndexModel

<h2>Index</h2>

@using (Html.BeginForm())
{
    <ul>
        <li>Name: @Html.EditorFor(m => m.Name[0])</li>
        <li>Name: @Html.EditorFor(m => m.Name[1])</li>
        <li>Name: @Html.EditorFor(m => m.Name[2])</li>
    </ul>

    <input type="submit" value="submit" />
}

Controller:

public class HomeController : Controller
{
    public ViewResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Index(IndexModel myIndex)
    {
        if (ModelState.IsValid)
        {
            return RedirectToAction("NextPage");
        }
        else
        {
            return View();
        }
    }
}

Model:

public class IndexModel
{
    public List<string> Name { get; set; }
}

Custom Model Binder:

public class IndexModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        bool hasPrefix = bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName);
        string searchPrefix = (hasPrefix) ? bindingContext.ModelName + "." : "";

        List<string> valueList = new List<string>();

        int index = 0;
        string value;

        do
        {
            value = GetValue(bindingContext, searchPrefix, "Name[" + index + "]");

            if (!string.IsNullOrEmpty(value))
            {
                valueList.Add(value);
            }

            index++;

        } while (value != null); //a null value indicates that the Name[index] field does not exist where as a "" value indicates that no value was provided.

        if (valueList.Count > 0)
        {
            //obtain the model object. Note: If UpdateModel() method was called the model will have been passed via the binding context, otherwise create our own.
            IndexModel model = (IndexModel)bindingContext.Model ?? new IndexModel();
            model.Name = valueList;
            return model;
        }

        //No model to return as no values were provided.
        return null;
    }

    private string GetValue(ModelBindingContext context, string prefix, string key)
    {
        ValueProviderResult vpr = context.ValueProvider.GetValue(prefix + key);
        return vpr == null ? null : vpr.AttemptedValue;
    }
}

You will need to register the model binder in the Application_Start() (global.asax):

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        //this will use your custom model binder any time you add the IndexModel to an action or use the UpdateModel() method.
        ModelBinders.Binders.Add(typeof(IndexModel), new IndexModelBinder());

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);
    }

Custom Validation Attribute

Alternatively, you could validate that all of the values are populated by using a custom attribute:

View:

@model MvcApplication3.Models.IndexModel

<h2>Index</h2>

@using (Html.BeginForm())
{
    @Html.ValidationMessageFor(m => m.Name)
    <ul>
        <li>Name: @Html.EditorFor(m => m.Name[0])</li>
        <li>Name: @Html.EditorFor(m => m.Name[1])</li>
        <li>Name: @Html.EditorFor(m => m.Name[2])</li>
    </ul>

    <input type="submit" value="submit" />
}

Controller:

Use the same controller defined above.

Model:

public class IndexModel
{
    [AllRequired(ErrorMessage="Please enter all required names")]
    public List<string> Name { get; set; }
}

Custom Attribute:

public class AllRequiredAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        bool nullFound = false;

        if (value != null && value is List<string>)
        {
            List<string> list = (List<string>)value;

            int index = 0;

            while (index < list.Count && !nullFound)
            {
                if (string.IsNullOrEmpty(list[index]))
                {
                    nullFound = true;
                }
                index++;
            }
        }

        return !nullFound;
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top