1

1つの基本クラスに2つのクラスがあるとしましょう:

public class BaseModel {

}

そして数人の子供たち:

public class FooModel : BaseModel {

}

public class BarModel : BaseModel {

}

今、私の見解では、次のようなモデルを期待したいと思います:

@model IEnumerable<BaseModel>

そして、私の行動では、次の行に沿って子クラスを渡します。

return View(new List<BaseModel>(){ new FooModel(), new BarModel() })

次に、これらを 1 つのページで編集しますEditFor(これは正常に動作します) 。

問題は、ポストバックするときに、これらの型を実装型にキャストできるようにしたいのですが、これは機能しません。キャストしようとすると、null になるか、例外がスローされます。

    [HttpPost]
    public ActionResult BaseModelUpdate(IList<BaseModel > model)
{
// I would like to access items in the list as FooModel and BarModel 
}

どうすればこれを達成できますか?(リスト内の項目を子クラスの型に戻しますか?)

TryUpdateModel を試してみることができると思っていましたか?

ご協力いただきありがとうございます。

4

1 に答える 1

2

コレクション内のアイテムのインデックスを指定する必要があります。

コントローラーのコードは次のとおりです。

public class HomeController : Controller
{
    //
    // GET: /Home/

    public ActionResult Index()
    {

        return 
            View(new List<BaseModel>() { new BarModel() { BaseProp = "Bar" }, new FooModel() { BaseProp = "Foo" } });
    }

    [HttpPost]
    public ActionResult Index(IList<BaseModel> model)
    {
        return this.View(model);
    }

}

ご覧のとおり、特別なことは何もありません。魔法はビューにあります:

@using MvcApplication1.Models
@model IList<MvcApplication1.Models.BaseModel>

@{
    ViewBag.Title = "title";
    //Layout = "_Layout";
}

<h2>title</h2>
@using (Html.BeginForm())
{
    for (int i = 0; i < Model.Count; i++)
     {

         @Html.EditorFor(p => p[i])
     }
    <input type="submit" value="Save" />
}

ご覧のとおり、EditorFor に渡される式には、コレクション内の現在の項目のインデックスが含まれています。これが必要な理由については、こちらで説明しています。簡単に言うと、EditorFor は、name 属性にコレクション内の項目のインデックスが含まれる各プロパティの入力要素を返します。

<input class="text-box single-line" name="[0].BaseProp" type="text" value="Bar" />

アップデート

オブジェクトのタイプを保持しようとしている場合は、特定のモデル タイプを格納する特別なプロパティがモデルに必要であり、IModelBinderそのプロパティに基づいて特定のモデル インスタンスを作成するカスタム実装が必要です。以下はモデルクラスです。プロパティは非表示のType入力としてレンダリングされます。

namespace MvcApplication1.Models
{
    using System.Web.Mvc;

    public class BaseModel
    {

        public string BaseProp { get; set; }

        [HiddenInput(DisplayValue = false)]
        public virtual string Type
        {
            get
            {
                return _type ?? this.GetType().FullName;
            }
            set
            {
                _type = value;
            }
        }
        private string _type;
    }

    public class FooModel : BaseModel
    {
        public string FooProp { get; set; }
    }

    public class BarModel :BaseModel
    {
        public string BarProp { get; set; }
    }
}

これは、カスタム モデル バインダーの実装例です。

    public class BaseModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            // call to get the BaseModel data so we can access the Type property
            var obj = base.BindModel(controllerContext, bindingContext);
            var bm = obj as BaseModel;
            if(bm != null)
            {
                //call base.BindModel again but this time with a new 
                // binding context based on the spefiic model type
                obj = base.BindModel(
                    controllerContext,
                    new ModelBindingContext(bindingContext)
                        {
                            ModelMetadata =
                                ModelMetadataProviders.Current.GetMetadataForType(null, Type.GetType(bm.Type)),
                                ModelName = bindingContext.ModelName
                        });
            }
            return obj;
        }
    }

application_start でカスタム バインダーを登録する必要があります。

ModelBinders.Binders.Add(typeof(BaseModel), new BaseModelBinder());
于 2012-10-29T01:42:02.517 に答える