25

タイプ用に独自のモデルバインダーを作成したいと思いDateTimeます。まず、次のようにモデルプロパティにアタッチできる新しい属性を記述します。

[DateTimeFormat("d.M.yyyy")]
public DateTime Birth { get; set,}

これは簡単な部分です。しかし、バインダー部分はもう少し難しいです。タイプの新しいモデルバインダーを追加したいと思いDateTimeます。私はどちらかができます

  • IModelBinderインターフェイスを実装し、独自のBindModel()メソッドを作成します
  • メソッドを継承しDefaultModelBinderてオーバーライドするBindModel()

私のモデルには、上記のようなプロパティがあります(Birth)。したがって、モデルがリクエストデータをこのプロパティにバインドしようとすると、モデルバインダーBindModel(controllerContext, bindingContext)が呼び出されます。すべて大丈夫ですが。日付を正しく解析するために、controller / bindingContextからプロパティ属性を取得するにはどうすればよいですか?どうすればPropertyDesciptor物件のに行くことができますBirthか?

編集

関心の分離のため、私のモデルクラスは、System.Web.MVCアセンブリを参照しない(そして参照すべきではない)アセンブリで定義されています。カスタムバインディング(Scott Hanselmanの例と同様)属性を設定することは、ここでは不要です。

4

4 に答える 4

87

IModelBinderを使用してユーザーカルチャを使用するようにデフォルトのモデルバインダーを変更できます

public class DateTimeBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);

        return value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);
    }
}

public class NullableDateTimeBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);

        return value == null
            ? null 
            : value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);
    }
}

そして、Global.Asaxで、Application_Start()に以下を追加します。

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new NullableDateTimeBinder());

Mvcフレームワークチームがすべてのユーザーにデフォルトのカルチャーを実装した理由を説明するこの優れたブログで詳細をお読みください。

于 2011-11-07T11:11:24.383 に答える
14

私自身もこの非常に大きな問題を抱えていました。何時間も試して失敗した後、あなたが尋ねたような実用的な解決策が得られました。

まず第一に、プロパティだけにバインダーを設定することは不可能なので、yuoは完全なModelBinderを実装する必要があります。すべての単一のプロパティをバインドするのではなく、気になるものだけをバインドする必要があるため、DefaultModelBinderから継承して、単一のプロパティをバインドできます。

public class DateFiexedCultureModelBinder : DefaultModelBinder
{
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
    {
        if (propertyDescriptor.PropertyType == typeof(DateTime?))
        {
            try
            {
                var model = bindingContext.Model;
                PropertyInfo property = model.GetType().GetProperty(propertyDescriptor.Name);

                var value = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);

                if (value != null)
                {
                    System.Globalization.CultureInfo cultureinfo = new System.Globalization.CultureInfo("it-CH");
                    var date = DateTime.Parse(value.AttemptedValue, cultureinfo);
                    property.SetValue(model, date, null);
                }
            }
            catch
            {
                //If something wrong, validation should take care
            }
        }
        else
        {
            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }
    }
}

私の例では、複雑な文化で日付を解析していますが、あなたがやりたいことは可能です。CustomAttribute(DateTimeFormatAttributeなど)を作成し、それをプロパティの上に置く必要があります。

[DateTimeFormat("d.M.yyyy")]
public DateTime Birth { get; set,}

これで、BindPropertyメソッドで、DateTimeプロパティを探す代わりに、DateTimeFormatAttributeでプロパティを探し、コンストラクターで指定した形式を取得して、DateTime.ParseExactで日付を解析できます。

これがお役に立てば幸いです。このソリューションを導入するのに非常に長い時間がかかりました。検索方法がわかれば、このソリューションを手に入れるのは実際には簡単でした:(

于 2010-06-11T06:33:36.240 に答える
3

モデルにロケール固有の属性を設定する必要はないと思います。

この問題に対する他の2つの可能な解決策は次のとおりです。

  • ページに日付をロケール固有の形式からJavaScriptのyyyy-mm-ddなどの一般的な形式に音訳させます。(動作しますが、JavaScriptが必要です。)
  • 日付を解析するときに現在のUIカルチャを考慮したモデルバインダーを記述します。

実際の質問に答えるために、カスタム属性(MVC 2の場合)を取得する方法は、AssociatedMetadataProviderを作成することです。

于 2010-03-01T15:22:12.897 に答える
0

このようにカスタムDateTimeバインダーを実装することもできますが、実際のクライアント要求から想定されるカルチャと値に注意する必要があります。en-USでmm/dd / yyyyのような日付を取得し、それをシステムカルチャen-GB(dd / mm / yyyyのようになります)または不変カルチャ(私たちのように)で変換したい場合は、前に解析し、静的ファサードConvertを使用して動作を変更する必要があります。

    public class DateTimeModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var valueResult = bindingContext.ValueProvider
                              .GetValue(bindingContext.ModelName);
            var modelState = new ModelState {Value = valueResult};

            var resDateTime = new DateTime();

            if (valueResult == null) return null;

            if ((bindingContext.ModelType == typeof(DateTime)|| 
                bindingContext.ModelType == typeof(DateTime?)))
            {
                if (bindingContext.ModelName != "Version")
                {
                    try
                    {
                        resDateTime =
                            Convert.ToDateTime(
                                DateTime.Parse(valueResult.AttemptedValue, valueResult.Culture,
                                    DateTimeStyles.AdjustToUniversal).ToUniversalTime(), CultureInfo.InvariantCulture);
                    }
                    catch (Exception e)
                    {
                        modelState.Errors.Add(EnterpriseLibraryHelper.HandleDataLayerException(e));
                    }
                }
                else
                {
                    resDateTime =
                        Convert.ToDateTime(
                            DateTime.Parse(valueResult.AttemptedValue, valueResult.Culture), CultureInfo.InvariantCulture);
                }
            }
            bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
            return resDateTime;
        }
    }

とにかく、ステートレスアプリケーションでのカルチャ依存のDateTime解析は、残酷なことになる可能性があります...特に、JavaScriptのクライアントサイドおよびバックワードでJSONを操作する場合はそうです。

于 2013-09-05T07:54:16.003 に答える