web-dev-qa-db-ja.com

ASP.NET MVCコントローラーで小数点を設定するにはどうすればよいですか?

私は NerdDinner アプリケーションでASP.NETMVCを自分自身に教えようとしています。しかし、グローバリゼーションの問題に遭遇しました。サーバーは小数点記号としてコンマを使用して浮動小数点数を表示しますが、Virtual Earthマップではドットを使用する必要があるため、いくつかの問題が発生します。

私はすでに解決しました 私のビューでのマッピングJavaScriptの問題 しかし、小数点としてドットを使用して編集されたディナーエントリを投稿しようとすると、コントローラーが失敗します(InvalidOperationExceptionをスローします)。モデルの更新(UpdateModel()メソッド内)。コントローラのどこかに適切なカルチャを設定する必要があるように感じます。OnActionExecuting()で試しましたが、それは役に立ちませんでした。

21
Pawel Krakowiak

私は実際のプロジェクトで問題を再検討し、ついに実用的な解決策を見つけました。適切な解決策は、タイプdecimal(およびdecimal?それらを使用している場合):

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

public class DecimalModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        object result = null;

        // Don't do this here!
        // It might do bindingContext.ModelState.AddModelError
        // and there is no RemoveModelError!
        // 
        // result = base.BindModel(controllerContext, bindingContext);

        string modelName = bindingContext.ModelName;
        string attemptedValue = bindingContext.ValueProvider.GetValue(modelName)?.AttemptedValue;

        // in decimal? binding attemptedValue can be Null
        if (attemptedValue != null)
        {
            // Depending on CultureInfo, the NumberDecimalSeparator can be "," or "."
            // Both "." and "," should be accepted, but aren't.
            string wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
            string alternateSeperator = (wantedSeperator == "," ? "." : ",");

            if (attemptedValue.IndexOf(wantedSeperator, StringComparison.Ordinal) == -1
                && attemptedValue.IndexOf(alternateSeperator, StringComparison.Ordinal) != -1)
            {
                attemptedValue = attemptedValue.Replace(alternateSeperator, wantedSeperator);
            }

            try
            {
                if (bindingContext.ModelMetadata.IsNullableValueType && string.IsNullOrWhiteSpace(attemptedValue))
                {
                    return null;
                }

                result = decimal.Parse(attemptedValue, NumberStyles.Any);
            }
            catch (FormatException e)
            {
                bindingContext.ModelState.AddModelError(modelName, e);
            }
        }

        return result;
    }
}

次に、Application_Start()のGlobal.asax.csで:

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

コードは私のものではないことに注意してください。実際、Kristof Neirynckのブログで見つけました ここ 。数行を編集して、デフォルトのバインダーを置き換えるのではなく、特定のデータ型のバインダーを追加しています。

64
Pawel Krakowiak

これをweb.configで設定します

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

小数点以下の桁数の代わりにコンマを使用する言語でセットアップされたサーバーを使用しているようです。 en-USなど、アプリケーションが設計されている方法でコンマを使用するカルチャに調整できます。

5
Nick Berardi

不変のカルチャを使用してテキストを解析できますか?申し訳ありませんが、NerdDinnerコードはありませんが、ドットで区切られた小数を渡す場合は、不変のカルチャを使用するように指示すれば、解析は問題ありません。 。例えば。

 float i = float.Parse("0.1", CultureInfo.InvariantCulture);

編集。ちなみに、これは以前の問題と同じ行に沿って、NerdDinnerコードのバグだと思います。

0
Steve

私はこれについて別の見方をしています、あなたはそれを好きかもしれません。受け入れられた答えについて私が気に入らないのは、他のキャラクターをチェックしないことです。ユーザーの方がよくわからないため、ボックスに通貨​​記号が表示される場合があります。そうですね、JavaScriptをチェックインして削除することはできますが、何らかの理由でJavaScriptがオンになっていない場合はどうなりますか?その後、余分な文字が通過する可能性があります。または、誰かがあなたにスパムを送信しようとした場合、未知の文字を通過させます...誰が知っていますか?そこで、正規表現を使用することにしました。それは少し遅く、ほんの少し遅くなります-私の場合、正規表現の1,000,000回の反復は3秒弱かかりましたが、コマとピリオドで文字列の置換を行うのに約1秒かかりました。しかし、どのキャラクターが登場するのかわからないので、このわずかなパフォーマンスのヒットに満足しています。

public class DecimalModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext,
                                     ModelBindingContext bindingContext)
    {
        string modelName = bindingContext.ModelName;
        string attemptedValue =
            bindingContext.ValueProvider.GetValue(modelName).AttemptedValue;

        if (bindingContext.ModelMetadata.IsNullableValueType
                && string.IsNullOrWhiteSpace(attemptedValue))
        {
            return null;
        }

        if (string.IsNullOrWhiteSpace(attemptedValue))
        {
            return decimal.Zero;
        }

        decimal value = decimal.Zero;
        Regex digitsOnly = new Regex(@"[^\d]", RegexOptions.Compiled);
        var numbersOnly = digitsOnly.Replace(attemptedValue, "");
        if (!string.IsNullOrWhiteSpace(numbersOnly))
        {
            var numbers = Convert.ToDecimal(numbersOnly);
            value = (numbers / 100m);

            return value;
        }
        else
        {
            if (bindingContext.ModelMetadata.IsNullableValueType)
            {
                return null;
            }

        }

        return value;
    }
}

基本的に、空でない文字列の場合は、数字ではないall文字を削除します。 10進数に変換します。 100で割ります。結果を返します。

私のために働きます。

0
Colin Wiseman