3

私が持っているとしましょう

class FooClass { }

class BarClass
{
    public FooClass Foo;
}

このBarClassは、ViewPageに渡すモデルです。

また、(ViewDataを介して)IEnumerable<SelectListItem>すべてのFooを含むを渡し、それに一致するものbar.Fooを選択します(実行時にチェックされます)。

私はそれから呼んでいますHtml.DropDownList("Foo", foos);

ドロップダウンリストは適切にレンダリングされますが、htmlコントロールにプロパティの名前があり、ViewData.Eval()内部で実行されるを混乱させるため、適切なアイテムが選択されません。それは受け入れられた振る舞いのようです(SOでそれについて多くの答えを見ました)ので、私はそれについて議論しておらず、拡張機能への呼び出しを次のように変更します:

Html.DropDownList("DDL_Foo", foos);

適切な値が選択され、私は満足しています。だから私はフォームを送り返します。

悲しいことに、私のコントローラーの適切なアクションでは、Fooメンバーはnullです。そこで、フォームのDDL_Fooをインターセプトし、FooClassを適切に初期化するためのFooModelBinder実装を追加します。IModelBinder

しかし、FooModelBinder.BindModel決して解雇さbar.Fooれることはなく、nullです。ビューを再度変更し、ドロップダウンリストの名前をFooに戻すと、FooModelBinderが期待どおりに起動し、bar.Fooが正常に初期化されます。

それで、私は何を逃しましたか?そしてもっと重要なのは、それを適切な方法でどのように行うべきかということです。私はたくさんのハックとその回避策を考え出しましたが、それは私が探しているものではありません。私はそれを正しく行う方法を知りたいです。

ありがとうございました!

[編集] フィードバックありがとうございますが、プレフィックスが問題になるとは思いません。

バインダーについては、正しく初期化できないので追加しました。私が取り組んでいる実際のケースは、ここに示されているものよりもはるかに複雑であることに注意してください。このソリューションは、問題を再現するために実行できる最小のモックアップです。

ここでそれは尋ねられた啓示的なコードです(または完全な解決策をダウンロードしてください):

コントローラ

    [HttpGet]
    public ActionResult Index()
    {
        var dp = new DummyProvider();
        var bar = dp.GetBar();
        var foos = new List<SelectListItem>();

        dp.GetAllFoos().ForEach(
            f => foos.Add(new SelectListItem {Text = f.Name, Value = f.Id.ToString(), Selected = f.Id == bar.Foo.Id }));

        ViewData["foos"] = foos;

        return View(bar);
    }

    [HttpPost]
    public ActionResult Index(BarClass bar)
    {
        var dp = new DummyProvider();
        var foos = new List<SelectListItem>();

        dp.GetAllFoos().ForEach(
            f => foos.Add(new SelectListItem { Text = f.Name, Value = f.Id.ToString(), Selected = f.Id == bar.Foo.Id }));

        ViewData["foos"] = foos;
        ViewData["selectedItem"] = bar.Foo.Name;

        return View(bar);
    }

見る

<%
    var foos = ViewData["foos"] as List<SelectListItem>;

    using(Html.BeginForm())
    {
        %>
        <p>
            <h3>Enter Another Value</h3>
            <%= Html.TextBox("AnotherValue", Model.AnotherValue) %>
        </p>
        <p>
            <h3>Enter Yet Another Value</h3>
            <%= Html.TextBox("YetAnotherValue", Model.YetAnotherValue) %>
        </p>

        <p>
            <h3>Choose a foo</h3>
            <%= Html.DropDownList("DDL_Foo", foos)%>
        </p>
        <button type="submit">Send back !</button>
        <%
    } 
%>

モデル

public class BarClass
{
    public FooClass Foo { get; set; }
    public string AnotherValue { get; set; }
    public string YetAnotherValue { get; set; }
}

public class FooClass
{
    public Guid Id { get; set; }
    public string Name { get; set; }

}

public class FooClassCollection : List<FooClass> { }

public class FooModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var foo = new FooClass();

        var guid = Guid.Empty;
        if (Guid.TryParse(controllerContext.HttpContext.Request.Form["DDL_Foo"], out guid))
        {
            foo.Id = guid;    
        }


        return foo;
    }
}

public class DummyProvider
{
    public FooClassCollection GetAllFoos()
    {
        return new FooClassCollection
                       {
                           new FooClass {Name = "Item 1", Id = new Guid("4a402abd-ab85-4065-94d6-d9fcc0f9b69e")},
                           new FooClass {Name = "Item 2", Id = new Guid("cf20bfd6-0918-4ffc-a6ec-c4cc4ed30e7f")},
                           new FooClass {Name = "Item 3", Id = new Guid("ad81b882-b93e-42b9-a42c-78376dd8f59d")},
                           new FooClass {Name = "Item 4", Id = new Guid("1511c15d-9ae4-4b18-9e10-e02588c21b27")},
                           new FooClass {Name = "Item 5", Id = new Guid("855e4a2f-fc5b-4117-a888-1dc3ebb990fc")},
                       };
    }

    public BarClass GetBar()
    {
        return new BarClass
                   {
                       AnotherValue = "Nice value",
                       YetAnotherValue = "This one is awesome",
                       Foo = new FooClass {Name = "Item 3", Id = new Guid("ad81b882-b93e-42b9-a42c-78376dd8f59d")}
                   };
    }
}

GLOBAL.ASAX

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);

        ModelBinders.Binders.Add(typeof(FooClass), new FooModelBinder());
    }

[編集] codeplexには未解決の問題があります。解決したい場合は、投票してください(1年近く開いていたとしても)。

4

2 に答える 2

1

すべての仕事をするBarClassModelBinderを作ることで、なんとかすべてを機能させることができました。コードは次のとおりです。

public class BarModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var bar = new BarClass();

        // In real code, check for nulls, etc.

        bar.AnotherValue = controllerContext.HttpContext.Request.Form["AnotherValue"];
        bar.YetAnotherValue = controllerContext.HttpContext.Request.Form["YetAnotherValue"];

        var guid = Guid.Empty;
        if (Guid.TryParse(controllerContext.HttpContext.Request.Form["DDL_Foo"], out guid))
        {
            bar.Foo = new FooClass {Id = guid};
        }


        return bar;
    }
}

So the only benefit I'm seeing there agains using FormCollection in the Controller is clarity of code. The only thing I'm not comfortable with is that the field name is "hidden" in the ModelBinder, so if someone changes the view, he has to be really careful on the field name. Maybe there is some way to circumvein that issue too, maybe with an attribute. But even without that, this is the lesser evil, so I'll settle with it.

The whole issue still look like a undesirable side effect of the DropDownListFor implementation.

于 2010-07-31T13:45:34.263 に答える
0

ちょうど30分ほどこれで遊んで過ごしました。カスタムモデルバインダーを作成することについては、あまり気にしません。全体FooClassではなく、Guid FooId代わりにビューモデルを使用します。とにかく、ドロップダウンリストからそれ以上のことは得られません。次に、これは機能します:

<%: Html.DropDownListFor(m => m.FooId, foos) %>

ポストバックすると、プロパティが正しくバインドされFooIdます。

ドメインモデルクラスの場合BarClass、ビューモデルは次のようになります(obv):

public class BarViewModel
{
    public Guid FooId { get; set; }
    public string AnotherValue { get; set; }
    public string YetAnotherValue { get; set; }
}
于 2010-07-31T03:54:29.507 に答える