MVC Model Binding to a collection where collection does not begin with a 0 index

做~自己de王妃 提交于 2019-11-30 06:54:49

You could write a custom model binder:

public class ImeiNumberModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var modelName = bindingContext.ModelName;
        var request = controllerContext.HttpContext.Request;
        var paramName = request
            .Params
            .Keys
            .Cast<string>()
            .FirstOrDefault(
                x => x.EndsWith(modelName, StringComparison.OrdinalIgnoreCase)
            );

        if (!string.IsNullOrEmpty(paramName))
        {
            return bindingContext
                .ValueProvider
                .GetValue(request[paramName])
                .AttemptedValue;
        }

        return null;
    }
}

and then apply it to the controller action:

public ActionResult IsImeiAvailable(
    [ModelBinder(typeof(ImeiNumberModelBinder))] string imeiNumber
)
{
    return Json(!string.IsNullOrEmpty(imeiNumber), JsonRequestBehavior.AllowGet);
}

Now the ImeiGadgets[xxx] part will be ignored from the query string.

If you are posting the whole collection, but have a nonsequential index, you could consider binding to a dictionary instead http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

If you're only posting a single item or using a GET link, then you should amend to

/Validation/IsImeiAvailable?ImeiNumber=123456789012345

and

public JsonResult IsImeiAvailable(string imeiNumber)

If you are sending up a single value to the server for validation, then your Action Method should only accept a scalar (single-value) parameter, not a collection. Your URL would then look like this (assuming default routing table for {controller}/{action}/{id}:

/Validation/IsImeiAvailable?ImeiNumber=123456789012345

the corresponding action method signature could look like this:

/* note that the param name has to matchthe prop name being validated */
public ActionResult IsImeiAvailable(int ImeiNumber)

EDIT: which you could then use to lookup whether that particular ID is available.

if you want to change the name of the parameter, you can modify the routing table, but that's a different topic.

The long story short of it is that if you wanted to do validate a collection of ImeiGadget, you'd GET or POST that full collection. For a single value, it doesn't make much sense to send up or to expect an entire collection.

UPDATE: Based on new info, I would look at where the remote validation attribute is being placed. It sounds like it might be placed on something like an IEnumerable<IMEiGadgets>, like this:

[Remote("IsImeiAvailable", "Validation", "'ImeiNumber' is invalid"]
public IEnumerable<ImeiGadget> ImeiGadgets { get; set;}

Would it be possible to move that attribute and modify it to be on the ImeiGadget class instead, to be something like this?

[Remote("IsImeiAvailable", "Validation", "'ImeiNumber is invalid"]
public int ImeiNumber { get; set;}

In theory, you shouldn't have to change anything on your HTML templates or scripts to get this working if you also make the change suggested in my answer above. In theory.

Unless you need this binding feature in many places and you control the IsImeiAvailable validation method then I think creating a custom model binder is an over-head.

Why don't you try a simple solution like this,

// need little optimization?
public JsonResult IsImeiAvailable(string imeiNumber)
{
  var qParam = Request.QueryString.Keys
     .Cast<string>().FirstOrDefault(a => a.EndsWith("ImeiNumber"));

  return Json(!string.IsNullOrEmpty(imeiNumber ?? Request.QueryString[qParam]), JsonRequestBehavior.AllowGet);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!