mvc3中带小数的错误 - 该值对字段无效

时间:2012-08-06 04:12:54

标签: asp.net-mvc asp.net-mvc-3 razor decimal model-binding

我正在关注[ASP.NET MVC 3入门] [1]。我无法使用Price = 9.99或9,99的值添加/编辑。它说:“价值'9.99'对价格无效。”和“现场价格必须是一个数字。”

如何解决这个问题?

型号:

    public class Movie
{
    public int ID { get; set; }
    public string Title { get; set; }
    public DateTime ReleaseDate { get; set; }
    public string Genre { get; set; }
    public decimal Price { get; set; }
}

public class MovieDbContext : DbContext
{
    public DbSet<Movie> Movies { get; set; }
}

控制器:

public class MovieController : Controller
{
    private MovieDbContext db = new MovieDbContext();

    //
    // GET: /Movie/

    public ViewResult Index()
    {
        var movie = from m in db.Movies
                     where m.ReleaseDate > new DateTime(1984, 6, 1)
                     select m;

        return View(movie.ToList()); 
    }

    //
    // GET: /Movie/Details/5

    public ViewResult Details(int id)
    {
        Movie movie = db.Movies.Find(id);
        return View(movie);
    }

    //
    // GET: /Movie/Create

    public ActionResult Create()
    {
        return View();
    } 

    //
    // POST: /Movie/Create

    [HttpPost]
    public ActionResult Create(Movie movie)
    {
        if (ModelState.IsValid)
        {
            db.Movies.Add(movie);
            db.SaveChanges();
            return RedirectToAction("Index");  
        }

        return View(movie);
    }

    //
    // GET: /Movie/Edit/5

    public ActionResult Edit(int id)
    {
        Movie movie = db.Movies.Find(id);
        return View(movie);
    }

    //
    // POST: /Movie/Edit/5

    [HttpPost]
    public ActionResult Edit(Movie movie)
    {
        if (ModelState.IsValid)
        {
            db.Entry(movie).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(movie);
    }

    //
    // GET: /Movie/Delete/5

    public ActionResult Delete(int id)
    {
        Movie movie = db.Movies.Find(id);
        return View(movie);
    }

    //
    // POST: /Movie/Delete/5

    [HttpPost, ActionName("Delete")]
    public ActionResult DeleteConfirmed(int id)
    {            
        Movie movie = db.Movies.Find(id);
        db.Movies.Remove(movie);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
}
}

查看:

    @model MvcMovies.Models.Movie

@{
ViewBag.Title = "Create";
}

<h2>Create</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript">       </script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
    <legend>Movie</legend>

    <div class="editor-label">
        @Html.LabelFor(model => model.Title)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Title)
        @Html.ValidationMessageFor(model => model.Title)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.ReleaseDate)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.ReleaseDate)
        @Html.ValidationMessageFor(model => model.ReleaseDate)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.Genre)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Genre)
        @Html.ValidationMessageFor(model => model.Genre)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.Price)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Price)
        @Html.ValidationMessageFor(model => model.Price)
    </div>

    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>
}

<div>
@Html.ActionLink("Back to List", "Index")
</div>
public DbSet<Movie> Movies { get; set; }
}

10 个答案:

答案 0 :(得分:36)

两年后我再次偶然发现了这一点。我认为ASP.NET MVC 5已经解决了这个问题,但看起来并非如此。所以这里是如何解决问题......

创建一个名为DecimalModelBinder的类,如下所示,并将其添加到项目的根目录中,例如:

using System;
using System.Globalization;
using System.Web.Mvc;

namespace YourNamespace
{   
    public class DecimalModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            ValueProviderResult valueResult = bindingContext.ValueProvider
                .GetValue(bindingContext.ModelName);

            ModelState modelState = new ModelState { Value = valueResult };

            object actualValue = null;

            if(valueResult.AttemptedValue != string.Empty)
            {
                try
                {
                    actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
                }
                catch(FormatException e)
                {
                    modelState.Errors.Add(e);
                }
            }

            bindingContext.ModelState.Add(bindingContext.ModelName, modelState);

            return actualValue;
        }
    }
}

内部Global.asax.cs,Application_Start()中使用它,如下所示:

ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());

答案 1 :(得分:6)

您是非英语客户之一,MS没有预见到。您需要付出额外的努力才能使您的版本运行。我有一个类似的问题,拒绝我将“9,99”和“9.99”作为有效数字。似乎一旦服务器端验证失败,并且一旦客户端验证,就不会接受任何数字。

所以你必须使验证一致。

喜欢在评论中提出建议,看看 http://msdn.microsoft.com/en-us/library/gg674880(VS.98).aspxhttp://haacked.com/archive/2010/05/10/globalizing-mvc-validation.aspxMVC 3 jQuery Validation/globalizing of number/decimal field 或 - 你应该了解德语(或只是看代码示例) http://www.andreas-reiff.de/2012/06/probleme-mit-mvcmovies-beispiel-validierung-des-preises-mit-dezimalstellen-schlagt-fehl/

BTW,音乐和电影示例教程都存在同样的问题。

答案 2 :(得分:2)

我在荷兰的Pc上为英语读者开发Web应用程序时遇到了这个问题。

double类型的模型属性,生成此服务器端验证错误:

  

值“1.5”无效。

在断点处,我在立即窗口中看到了这些值:

?System.Threading.Thread.CurrentThread.CurrentUICulture
  

{的en-US}

?System.Threading.Thread.CurrentThread.CurrentCulture
  

{NL-NL}

作为解决方案(或可能是解决方法),您可以在web.config文件中指定全球化设置。

<configuration>
  <system.web>
    <globalization culture="en" uiCulture="en" />

当然这意味着您强制用户输入英文格式的数字,但就我而言,这很好。

答案 3 :(得分:1)

我通过禁用jquery价格解决了这个问题,并且仅在服务器端验证该输入。我在这里找到了答案: ASP .NET MVC Disable Client Side Validation at Per-Field Level

$(".hamburger").click(function(){

if ($(".hamburger").css("display") === "inline-block" ){
    $("#mobileMenu").fadeToggle();
} else {
    $("#mobileMenu").remove();
}

});

答案 4 :(得分:1)

我已经调整了Leniel Macaferi的代码,所以你可以将它用于任何类型:

public class RequestModelBinder<TBinding> : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        ValueProviderResult valueResult = bindingContext.ValueProvider
            .GetValue(bindingContext.ModelName);

        ModelState modelState = new ModelState { Value = valueResult };

        object actualValue = null;

        if (valueResult.AttemptedValue != string.Empty)
        {
            try
            {
                // values really should be invariant
                actualValue = Convert.ChangeType(valueResult.AttemptedValue, typeof(TBinding), CultureInfo.CurrentCulture);
            }
            catch (FormatException e)
            {
                modelState.Errors.Add(e);
            }
        }

        bindingContext.ModelState.Add(bindingContext.ModelName, modelState);

        return actualValue;
    }
}

答案 5 :(得分:1)

我尝试过 @Leniel Macaferi ,但它对我不起作用。

ModelState.IsValid不接受格式为7.000,00

的数字

当我从以下位置更改属性类型时,问题就开始了:

[Column("PRICE")]
public decimal Price { get; set; }

[Column("PRICE")]
public decimal? Price { get; set; }

我也尝试将全球化纳入我忘记的web.config

<globalization culture="pt-BR" uiCulture="pt-BR" enableClientBasedCulture="true" />

唯一有效的解决方法是将属性更改为仅十进制:

[Column("PRICE")]
public decimal Price { get; set; }

并且还将表列更改为NOT接受空值

希望它对某人有帮助。

答案 6 :(得分:0)

2019年,此问题仍未解决。使用ASP Core 2.1,我的UI使用法语(小数点分隔符=','),并且只要有小数点,我就无法使验证工作。

我发现了一种变通方法,但并不理想:我创建了一个基于法语的CultureInfo,但我将小数点分隔符更改为与Invariant Culture中的“。”相同。

这成功了,我的十进制数字现在显示为美式风格(但我可以接受)并且验证有效。

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseCookiePolicy();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });


        //Culture specific problems
        var cultureInfo = new CultureInfo("fr-FR");
        cultureInfo.NumberFormat.NumberDecimalSeparator = ".";
        System.Threading.Thread.CurrentThread.CurrentUICulture = cultureInfo;

    }

答案 7 :(得分:0)

这是 Leniel Maccaferri 解决方案的扩展,可避免与用户代理发送与服务器不同文化格式的十进制值相关的问题。它的限制受限于千位分隔符解析,当它是值中的唯一分隔符时,可能会引发错误绑定。

/// <summary>
/// custom decimal model binder
/// </summary>
/// <author>https://stackoverflow.com/users/114029/leniel-maccaferri</author>
/// <see cref="https://stackoverflow.com/a/19339424/3762855"/>
/// <remarks>Integrated with a fix for the decimal separator issue.
/// <para>This issue maybe depends from browsers interpretation of decimal values posted-back to the server when they receive response without any http content-language specific indication.</para>
/// <para>Important! decimal values caming from UI must not be formatted with thousands separator.</para>
/// </remarks>
public class DecimalModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        ValueProviderResult valueResult = bindingContext.ValueProvider
            .GetValue(bindingContext.ModelName);

        ModelState modelState = new ModelState { Value = valueResult };

        object actualValue = null;

        if (valueResult.AttemptedValue != string.Empty)
        {
            try
            {
                var culture = Thread.CurrentThread.CurrentCulture;

                //This is needed to convert in the right manner decimal values coming from UI, as seems they always represent the decimal separator as a period("."). 
                //Maybe depends from browsers interpretation of decimal values posted-back to the server when they receive response without any http content-language specific indication.
                if (culture.NumberFormat.NumberDecimalSeparator == "," && valueResult.AttemptedValue.LastIndexOf(".") > 0)
                {
                    culture = new CultureInfo("en"); 
                }
                else if (Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator == "." && valueResult.AttemptedValue.LastIndexOf(",") > 0)
                {
                    culture = new CultureInfo("it");
                }

                actualValue = Convert.ToDecimal(valueResult.AttemptedValue, culture);

            }
            catch (FormatException e)
            {
                modelState.Errors.Add(e);
            }
        }

        bindingContext.ModelState.Add(bindingContext.ModelName, modelState);

        return actualValue;
    }
}

答案 8 :(得分:-1)

您可以添加:

protected void Application_BeginRequest()
{
    var currentCulture = (CultureInfo)CultureInfo.CurrentCulture.Clone();
    currentCulture.NumberFormat.NumberDecimalSeparator = ".";
    currentCulture.NumberFormat.NumberGroupSeparator = " ";
    currentCulture.NumberFormat.CurrencyDecimalSeparator = ".";

    Thread.CurrentThread.CurrentCulture = currentCulture;
    //Thread.CurrentThread.CurrentUICulture = currentCulture;
}

Global.asax(在MVC 5.1上测试)。它可以在不改变UICulture的情况下工作。

答案 9 :(得分:-4)

只需为脚本注释此链接:

<%--<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>--%>