How to validate my model in a custom model binder?

前端 未结 3 928
有刺的猬
有刺的猬 2020-12-31 05:44

I asked about an issue I have with comma delimited numeric values here.

Given some of the replies, I attempted to try to implement my own model binder as follows:

相关标签:
3条回答
  • 2020-12-31 06:19
        [HttpPost]
        public ActionResult Edit([ModelBinder(typeof(PropertyModelBinder))]PropertyModel model)
        {
            ModelState.Clear();
            TryValidateModel(model);
            if (ModelState.IsValid)
            {
                //Save property info.              
            }
    
            return View(model);
        }
    

    Hope This will Help.

    Also you can try @Ryan solution as well.

    This could be your Custom ModelBinder. ( In this case you don't need to update your Edit Action Result As I suggested above)

    public class PropertyModelBinder : DefaultModelBinder
    {     
    
        protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
        {
            if(propertyDescriptor.ComponentType == typeof(PropertyModel))
            {
                if (propertyDescriptor.Name == "Price")
                {
                    var obj=   bindingContext.ValueProvider.GetValue("Price");
                    return Convert.ToInt32(obj.AttemptedValue.ToString().Replace(",", ""));
                }
            }
            return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
        }       
    }
    

    As you have updated your scope for binding. I have provided my suggestion in comments. Also if you use ModelBinder for Property and Agent than you can do like this.

    //In Global.asax
    ModelBinders.Binders.Add(typeof(Property), new PropertyRegistrationModelBinder());
    ModelBinders.Binders.Add(typeof(Agent), new PropertyRegistrationModelBinder());
    
    //Updated ModelBinder look like this.
    
     public class PropertyRegistrationModelBinder : DefaultModelBinder
    {
        protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
        {
            if (propertyDescriptor.ComponentType == typeof(Property) || propertyDescriptor.ComponentType == typeof(Agent))
            {
                if(propertyDescriptor.Name == "Price" || propertyDescriptor.Name == "AnnualSales")
                {                    
                    var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue.Replace(",", string.Empty);
                    return string.IsNullOrEmpty(value) ? 0 : Convert.ToInt32(value);
                }
            }            
            return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
        }
    } 
    

    Also I would like to say that you are able to find many information related to this and also you can do same thing many ways. Like if you introduce new attribute that apply to class property for binding same way you apply ModelBinder at class level.

    0 讨论(0)
  • 2020-12-31 06:28

    I got your validation to work fine by changing when BindModel fires. In your code, you have these lines in PropertyModelBinder:

    object o = base.BindModel(controllerContext, newBindingContext);
    newBindingContext.ModelState.Remove("Price");
    newBindingContext.ModelState.Add("Price", new ModelState());
    newBindingContext.ModelState.SetModelValue("Price", new ValueProviderResult(price, price, null));
    return o;
    

    I moved base.BindModel to fire immediately before returning the object (after reconstructing context) and now validation works as expected. Here is the new code:

    newBindingContext.ModelState.Remove("Price");
    newBindingContext.ModelState.Add("Price", new ModelState());
    newBindingContext.ModelState.SetModelValue("Price", new ValueProviderResult(price, price, null));
    object o = base.BindModel(controllerContext, newBindingContext);
    return o;
    
    0 讨论(0)
  • 2020-12-31 06:31

    This won't exactly answer your question, but I'm putting it as an answer anyway because I think it addresses what people were asking you about in your previous question.

    In this particular instance, it really sounds as though you want a string. Yes, I know the values are, ultimately, numerical (integer, it seems) values, but when you are working with something like MVC, you have to remember that the models that are being used by your view do not have to match the models that are part of your business logic. I think you're running into this issue because you are attempting to mix the two.

    Instead, I would recommend creating a model (a ViewModel) that is specifically for the View that you are displaying to the end user. Add the various data annotations to it that will help validate the STRING that makes up the Price. (You could easily do all the validation you want via a simple regular expression data annotation. Then, once the model is actually submitted to your controller (or whatever other data is submitted to your controller) and you know that it is valid (via the data annotation), you can then convert it to an integer that you want for the model you are using with your business logic, and go on using it as you would (as an integer).

    This way, you avoid having all this unnecessary complexity that you have been asking about (which does have a solution, but it doesn't really fit the mindset behind MVC) and allows you to achieve the flexibility you are looking for in your view with the stringent requirement in your business logic.

    Hopefully that makes sense. You can search the web for ViewModels in MVC. A good place to start out is the ASP.NET MVC tutorials.

    0 讨论(0)
提交回复
热议问题