38

次のコントローラーアクションがあります。

[HttpPost]
public ViewResult DoSomething(MyModel model)
{
    // do something
    return View();
}

MyModelこのように見える場所:

public class MyModel
{
    public string PropertyA {get; set;}
    public IList<int> PropertyB {get; set;}
}

したがって、DefaultModelBinderはこれを問題なくバインドする必要があります。唯一のことは、バインディングに特別な/カスタムバインダーを使用したいということですPropertyB。また、このバインダーを再利用したいと思います。したがって、解決策は、もちろん機能しないPropertyBの前にModelBinder属性を配置することだと思いました(ModelBinder属性はプロパティでは許可されていません)。私は2つの解決策を見ます:

  1. モデル全体ではなく、すべてのプロパティでアクションパラメータを使用するには(モデルには多くのプロパティがあるため、これは好ましくありません)、次のようになります。

    public ViewResult DoSomething(string propertyA, [ModelBinder(typeof(MyModelBinder))] propertyB)
    
  2. 新しいタイプを作成するにMyCustomType: List<int>は、このタイプのモデルバインダーを登録します(これはオプションです)

  3. たぶん、MyModelのバインダーを作成し、オーバーライドBindPropertyして、プロパティが"PropertyB"カスタムバインダーでプロパティをバインドしている場合。これは可能ですか?

他に解決策はありますか?

4

4 に答える 4

23

BindProperty をオーバーライドし、プロパティが "PropertyB" の場合、プロパティをカスタム バインダーにバインドします。

これは良い解決策ですが、「is PropertyB」をチェックする代わりに、次のようなプロパティ レベルのバインダーを定義する独自のカスタム属性をチェックすることをお勧めします。

[PropertyBinder(typeof(PropertyBBinder))]
public IList<int> PropertyB {get; set;}

ここで BindProperty オーバーライドの例を確認できます。

于 2010-02-03T10:43:22.233 に答える
19

DefaultModelBinder私は実際にあなたの3番目のソリューションが好きですが、MVCアプリケーションのデフォルトのモデルバインダーを継承して構成されたカスタムバインダーに配置することで、すべてのModelBindersの汎用ソリューションにします。

次に、パラメーターで指定された型を使用して、属性で装飾された任意のプロパティをこの新しいものDefaultModelBinderに自動的にバインドさせます。PropertyBinder

この優れた記事からアイデアを得ました: http://aboutcode.net/2011/03/12/mvc-property-binder.html

また、ソリューションに対する私の見解も示します。

私のDefaultModelBinder

namespace MyApp.Web.Mvc
{
    public class DefaultModelBinder : System.Web.Mvc.DefaultModelBinder
    {
        protected override void BindProperty(
            ControllerContext controllerContext, 
            ModelBindingContext bindingContext, 
            PropertyDescriptor propertyDescriptor)
        {
            var propertyBinderAttribute = TryFindPropertyBinderAttribute(propertyDescriptor);
            if (propertyBinderAttribute != null)
            {
                var binder = CreateBinder(propertyBinderAttribute);
                var value = binder.BindModel(controllerContext, bindingContext, propertyDescriptor);
                propertyDescriptor.SetValue(bindingContext.Model, value);
            }
            else // revert to the default behavior.
            {
                base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
            }
        }

        IPropertyBinder CreateBinder(PropertyBinderAttribute propertyBinderAttribute)
        {
            return (IPropertyBinder)DependencyResolver.Current.GetService(propertyBinderAttribute.BinderType);
        }

        PropertyBinderAttribute TryFindPropertyBinderAttribute(PropertyDescriptor propertyDescriptor)
        {
            return propertyDescriptor.Attributes
              .OfType<PropertyBinderAttribute>()
              .FirstOrDefault();
        }
    }
}

私のIPropertyBinderインターフェース:

namespace MyApp.Web.Mvc
{
    interface IPropertyBinder
    {
        object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext, MemberDescriptor memberDescriptor);
    }
}

私のPropertyBinderAttribute

namespace MyApp.Web.Mvc
{
    public class PropertyBinderAttribute : Attribute
    {
        public PropertyBinderAttribute(Type binderType)
        {
            BinderType = binderType;
        }

        public Type BinderType { get; private set; }
    }
}

プロパティ バインダーの例:

namespace MyApp.Web.Mvc.PropertyBinders
{
    public class TimeSpanBinder : IPropertyBinder
    {
        readonly HttpContextBase _httpContext;

        public TimeSpanBinder(HttpContextBase httpContext)
        {
            _httpContext = httpContext;
        }

        public object BindModel(
            ControllerContext controllerContext,
            ModelBindingContext bindingContext,
            MemberDescriptor memberDescriptor)
        {
            var timeString = _httpContext.Request.Form[memberDescriptor.Name].ToLower();
            var timeParts = timeString.Replace("am", "").Replace("pm", "").Trim().Split(':');
            return
                new TimeSpan(
                    int.Parse(timeParts[0]) + (timeString.Contains("pm") ? 12 : 0),
                    int.Parse(timeParts[1]),
                    0);
        }
    }
}

使用されている上記のプロパティ バインダーの例:

namespace MyApp.Web.Models
{
    public class MyModel
    {
        [PropertyBinder(typeof(TimeSpanBinder))]
        public TimeSpan InspectionDate { get; set; }
    }
}
于 2012-10-02T01:03:13.380 に答える
6

@jonathanconwayの答えは素晴らしいですが、細部を追加したいと思います。

検証メカニズムが機能する機会を与えるためではGetPropertyValueなく、メソッドをオーバーライドする方がおそらく良いでしょう。BindPropertyDefaultBinder

protected override object GetPropertyValue(
    ControllerContext controllerContext,
    ModelBindingContext bindingContext,
    PropertyDescriptor propertyDescriptor,
    IModelBinder propertyBinder)
{
    PropertyBinderAttribute propertyBinderAttribute =
        TryFindPropertyBinderAttribute(propertyDescriptor);
    if (propertyBinderAttribute != null)
    {
        propertyBinder = CreateBinder(propertyBinderAttribute);
    }

    return base.GetPropertyValue(
        controllerContext,
        bindingContext,
        propertyDescriptor,
        propertyBinder);
}
于 2013-11-01T13:23:57.773 に答える
1

この質問が出されてから 6 年が経ちました。まったく新しい解決策を提供するのではなく、このスペースを使って更新を要約したいと思います。これを書いている時点で、MVC 5 はかなり前から存在しており、ASP.NET Core が登場したばかりです。

私は、Vijaya Anand (ところで、Vijaya に感謝) によって書かれた投稿で検討されたアプローチに従いました: http://www.prideparrot.com/blog/archive/2012/6/customizing_property_binding_through_attributes。注目すべき点の 1 つは、データ バインディング ロジックがカスタム属性クラスに配置されていることです。これは、Vijaya Anand の例の StringArrayPropertyBindAttribute クラスの BindProperty メソッドです。

ただし、私が読んだこのトピックに関する他のすべての記事 (@jonathanconway のソリューションを含む) では、カスタム属性クラスは、フレームワークが適用する正しいカスタム モデル バインダーを見つけるための足がかりにすぎません。バインド ロジックは、通常は IModelBinder オブジェクトであるカスタム モデル バインダーに配置されます。

最初のアプローチは私にとってより簡単です。最初のアプローチにはまだ知られていないいくつかの欠点があるかもしれませんが、現時点では MVC フレームワークにかなり慣れていないためです。

さらに、Vijaya Anand の例の ExtendedModelBinder クラスは MVC 5 では不要であることがわかりました。MVC 5 に付属する DefaultModelBinder クラスは、カスタム モデル バインディング属性と連携するのに十分スマートであるようです。

于 2016-12-22T02:07:10.703 に答える