0

I'm making a form to work with a table that had some fields set as decimal(18, 4) to store financial datas.

When I try to populate the fields with my form, the model is considered valid if the values are integers, but it isn't if the values have decimals. To work around the globalization issues, I have a set of regular text fields where people can type in the numbers and parse them into hidden fields with some javascript:

<div class="row text-center">
    <label class="col-sm-2 col-form-label font-weight-bold">Budget A</label>
    <div class="col">
        <input type="text" class="form-control form-control-sm BudgetField text-right" data-type="A" data-family="1" name="BudgetTextA1" value="0" required />
    </div>
    <div class="col">
        <input type="text" class="form-control form-control-sm BudgetField text-right" data-type="A" data-family="2" name="BudgetTextA2" value="0" required />
    </div>
    <div class="col">
        <input type="text" class="form-control form-control-sm BudgetField text-right" data-type="A" data-family="3" name="BudgetTextA3" value="0" required />
    </div>
    <div class="col">
        <input type="text" class="form-control form-control-sm BudgetField text-right" data-type="A" data-family="4" name="BudgetTextA4" value="0" required />
    </div>
</div>
@Html.HiddenFor(m => m.BudgetA1)
@Html.HiddenFor(m => m.BudgetA2)
@Html.HiddenFor(m => m.BudgetA3)
@Html.HiddenFor(m => m.BudgetA4)
<script>
    // https://stackoverflow.com/questions/469357/html-text-input-allow-only-numeric-input -> https://jsfiddle.net/emkey08/zgvtjc51
    function setInputFilter(textbox, inputFilter) {
        ["input", "keydown", "keyup", "mousedown", "mouseup", "select", "contextmenu", "drop"].forEach(function (event) {
            textbox.addEventListener(event, function () {
                if (inputFilter(this.value)) {
                    this.oldValue = this.value;
                    this.oldSelectionStart = this.selectionStart;
                    this.oldSelectionEnd = this.selectionEnd;
                } else if (this.hasOwnProperty("oldValue")) {
                    this.value = this.oldValue;
                    this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd);
                } else {
                    this.value = "";
                }
            });
        });
    }

    $(document).ready(function () {
        $('.BudgetField').each(function () {
            var lBudgetCode = $(this).data('type') + $(this).data('family');
            var lBudgetField = $('#Budget' + lBudgetCode).val();
            if (lBudgetField != 0) {
                $('input[name=BudgetText' + lBudgetCode + ']').val(parseFloat(lBudgetField).toFixed(2));
            }
        });

        $('.BudgetField').on('change', function () {
            setInputFilter(this, function (value) {
                return /^-?\d*[.,]?\d*$/.test(value);
            });
            lBudget = 0;
            if (this.value == '') {
                this.value = 0;
            }
            $('.BudgetField').each(function () {
                lBudget += Number(this.value);
                $('#Budget' + $(this).data('type') + $(this).data('famille')).val(parseFloat(this.value));
            });
        });
    });
</script>

My controller does some rather simple stuff:

public ActionResult BudgetForm(SomeObject pFormObject)
{
    if (ModelState.IsValid)
    {
        db.Entry(pFormObject).State = EntityState.Modified;
        try
        {
            db.SaveChanges();
        }
        catch (DbEntityValidationException e)
        {
            /* do some exception handling */
        }
    return RedirectToAction("Index", "Identification");
    }
    return View(pFormObject);
}

The model is generated from the database, I merely added some metadata in a separate file for display formatting:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Manual changes to this file may cause unexpected behavior in your application.
//     Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace WebApp.Models
{
    using System;
    using System.Collections.Generic;
    
    public partial class SomeObject
    {
        public System.Guid ID { get; set; }
        (...)
        public Nullable<decimal> BudgetA1 { get; set; }
        public Nullable<decimal> BudgetA2 { get; set; }
        public Nullable<decimal> BudgetA3 { get; set; }
        public Nullable<decimal> BudgetA4 { get; set; }
    }
}

Metadata file

using System;
using System.ComponentModel.DataAnnotations;

namespace WebApp.Models
{
    [MetadataType(typeof(SomeObjectMetadata))]
    public partial class SomeObject
    {
    }

    public class SomeObjectMetadata
    {
        (...)

        [DisplayFormat(DataFormatString = "{0:C}")]
        public decimal BudgetA1;

        [DisplayFormat(DataFormatString = "{0:C}")]
        public decimal BudgetA2;

        [DisplayFormat(DataFormatString = "{0:C}")]
        public decimal BudgetA3;

        [DisplayFormat(DataFormatString = "{0:C}")]
        public decimal BudgetA4;
    }
}

If the user types in 8400 in one of the fields, I get 8400M as value in the object the controller receives. If the user types in 8400.04 in one of the fields, I see in that value in Chrome's DevTools for both the text field's value, and the hidden field value as data sent in the POST request, but I get null value for if I inspect the object properties the controller receives in the debugger and the controller rightfully returns me the form with the data in the fields.

What am I missing?

  • Hard to say without the html - seems like it's having trouble binding the result back to ModelType suspect that the name of ModelType is confusing because it's also a part of .net https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.modelbindingcontext.modeltype?view=aspnet-mvc-5.2 Maybe try a different name for your table and the binding might work. Also would steer clear of the 'database first' and go for 'code first'. I find it a lot easier and without headaches. – Richard Housham Aug 05 '20 at 12:09
  • 1
    My bad, ModelType was a poor choice, it was just a placeholder I used here to replace a non-english term, it wasn't the actual code, i edited the question to reflect it. I nailed it though, it was some locale mismatch, forcing `` in web.config solves it. –  Aug 06 '20 at 07:38
  • well done on solving it - add your solution to the answers and then you can mark it as answered in a day or two . Might help some other person – Richard Housham Aug 07 '20 at 14:16

0 Answers0