Cristian Diaconescu
Cristian Diaconescu

Reputation: 35701

ASP.NET MVC Require positive number as input, save as negative in model - validation fails

I want to model an 'Expense' object that has a 'Sum' (decimal) field. In the view, I want to validate that the user enters a positive value.

OTOH I want to make sure I save the object with a negative value in the DB.

Right now, the model looks like this:

//------The model-------
public class Operation {
    [Range(typeof(decimal), "0.0001", "79228162514264337593543950335")]
    public virtual decimal Sum { get; set; }
    [...]
}
public class Expense : Operation
{
    public override decimal Sum
    {
        get
        {
            return base.Sum;
        }
        set
        {
            base.Sum = - Math.Abs(value);
        }
    }
}

//------In the controller-------
[HttpPost]
public ActionResult CreateExpense(Expense operation, int[] SelectedTags)
{
    return CreatePost(operation, SelectedTags);
}
private ActionResult CreatePost(Operation operation, int[] SelectedTags)
{
    if (ModelState.IsValid)  //  <-- this fails
    [...]
}

The problem is, the MVC validation works with the object's properties (not the POST'ed form values), sees the negative value and fails to validate.

What should I do to fix this?

It looks to me like I'm not separating concerns (validate user input vs maintain database integrity).

Should I use a view model to hold the user input and then populate the actual model from the view model? Doesn't sound like KISS...

Upvotes: 2

Views: 8237

Answers (2)

Qudus
Qudus

Reputation: 1520

A validation attribute to check if the value is less than zero is another simple solution.

public class PositiveNumberAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object number, ValidationContext validationContext)
    {
        return int.Parse(number.ToString()) >= 0
            ? ValidationResult.Success : new ValidationResult("Positive value required.");
    }
}

Then apply to property

[PositiveNumber]
public virtual decimal Sum { get; set; }

Upvotes: 0

Cristian Diaconescu
Cristian Diaconescu

Reputation: 35701

I found out that specifying a separate validation attribute on the property of the inherited class works a treat.

Can't think of something more straight-forward.

Here's how the model looks like now:

public class Operation {
    public virtual decimal Sum { get; set; }
}
public class Income : Operation
{
    [Range(typeof(decimal), "0.0001", "79228162514264337593543950335")]
    public override decimal Sum
    {
        get { return base.Sum; }
        set { base.Sum = Math.Abs(value); }
    }
}

public class Expense : Operation
{
    [Range(typeof(decimal), "-79228162514264337593543950335", "-0.0001")]
    public override decimal Sum
    {
        get { return base.Sum; }
        set { base.Sum = - Math.Abs(value); }
    }
}

Upvotes: 5

Related Questions