asp.net-mvc-3validationseparation-of-concerns

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...


Solution

  • 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); }
        }
    }