Is it correct way to use ModelState.Remove to deal with ModelState?

后端 未结 5 823
挽巷
挽巷 2020-12-05 14:17

Im working on a big MVC3 web application and have an annoyance regarding the ModelState.IsValid method.

ModelState is being used in nearly all of my con

相关标签:
5条回答
  • 2020-12-05 14:49

    I'm totally with Mr.Steve Morgan

    So if your ViewModel doesn't always need some property to be Required then you shouldn't decorate it as Required.

    I don't know why you wanna this issue but I suppose that in some cases you need PropertyOne to be Required if PropertyTwo has value. In this case you may need to make your CustomValidationAttribute to check these two properties.

    I'm using something like this :

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public class PropertyNeededAttribute : ValidationAttribute
    {
        private const string defaultErrorMessage = "'{0}' needs '{1}' to be valid.";
    
        public PropertyNeededAttribute(string originalProperty, string neededProperty)
            : base(defaultErrorMessage)
        {
            NeededProperty = neededProperty;
            OriginalProperty = originalProperty;
        }
    
        public string NeededProperty { get; private set; }
        public string OriginalProperty { get; private set; }
    
        public override object TypeId
        {
            get { return new object(); }
        }
    
        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
                                 OriginalProperty, NeededProperty);
        }
    
        public override bool IsValid(object value)
        {
            object neededValue = Statics.GetPropertyValue(value, NeededProperty);
            object originalValue = Statics.GetPropertyValue(value, OriginalProperty);
            if (originalValue != null && neededValue == null)
                return false;
            return true;
        }
    }
    

    note: Statics.GetPropertyValue(...) do nothing but get the value from the property to compare it.

    Hope this helped :)

    0 讨论(0)
  • 2020-12-05 14:58

    Here's my solution - a RemoveFor() extension method on ModelState, modelled after MVC HTML helpers:

        public static void RemoveFor<TModel>(this ModelStateDictionary modelState, 
                                             Expression<Func<TModel, object>> expression)
        {
            string expressionText = ExpressionHelper.GetExpressionText(expression);
    
            foreach (var ms in modelState.ToArray())
            {
                if (ms.Key.StartsWith(expressionText + ".") || ms.Key == expressionText)
                {
                    modelState.Remove(ms);
                }
            }
        }
    

    Here's how it's used :

    if (model.CheckoutModel.ShipToBillingAddress == true) 
    {
        // REUSE BILLING ADDRESS FOR SHIPPING ADDRESS
        ShoppingCart.ShippingAddress = ShoppingCart.BillingAddress;
    
        // REMOVE MODELSTATE ERRORS FOR SHIPPING ADDRESS
        ModelState.RemoveFor<SinglePageStoreModel>(x => model.CheckoutModel.ShippingAddress);
    }
    

    So in answer to your question I believe there are definitely use-cases where this is the right way to do it, and a strongly typed helper like this makes it much nicer to look at - and easier to justify if you're concerned about lots of magic strings.

    0 讨论(0)
  • 2020-12-05 15:01

    If your property is not always required you should not decorate it with [Required].

    A good alternative to do your validation is to implement the interface IValidatableObject.

    For example, let's say that you want to make the field State required only when the country is United States. You could do it that way :

    public class AddressModel : IValidatableObject
    {
        [Required]
        public string Country { get; set; }
        public string State { get; set; }
    
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if(Country == "United States" && String.IsNullOrEmpty(State))
            {
                yield return new ValidationResult("State is required for United States", new [] { nameof(State) });
            }
        }
    }
    

    Note : This kind of validation only works on the server side.

    Other alternatives?

    As mentioned in other answers, it's sometimes a good idea to create 2 or more models if the views and validations are very different.

    0 讨论(0)
  • 2020-12-05 15:05

    Fundamentally, your issue is that while your classes are decorated with [Required], it's not always true. If you're operating in a context where it isn't true, you should really be using a class that doesn't define the property as being [Required].

    You should really use a ViewModel that is correctly defined for its specific usage and that may mean duplicating some classes. A ViewModel is associated with the implementation of the UI and while it may use classes from your domain model, it's not always the right thing to do.

    Failing that, the options are to either not use ModelState.IsValid, or to continue to use ModelState.Remove.

    But logically, it makes sense for your ViewModel to be 'validatable', not to have to ignore certain validation errors.

    0 讨论(0)
  • 2020-12-05 15:07

    If you're using the same view model with a [Required] property in two different contexts, one where the property is required and one where it isn't, then you'll need to manually alter the ModelState as you're doing.

    An alternative is to use a different view model. Perhaps have a base class with all properties except the required property in question. Then derive two view models from it, one with the property where is it required and one with the property where it is not (it's duplication, I know). You may decide to keep them entirely separate altogether and not use inheritance.

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