将多个模型从一个视图传递到控制器

时间:2014-04-27 10:46:43

标签: asp.net .net asp.net-mvc asp.net-mvc-4 asp.net-mvc-5

如果我在一个视图中搜索“多个模型”,我只能找到有关如何将模型传递给视图的结果。但我对“从视图到控制器”的方向感兴趣。

所以我们假设:

  • 我在一个视图中有3种不同的表单和1个表(WebGrid)。
  • 我每张表格都有一个型号,桌子上有一个型号。
  • 让我的模型类为ModelF1ModelF2ModelF3ModelT

我到目前为止看到的所有示例都使用容器ViewModel,如

class MyViewModel {
    ModelF1 inst1,
    ModelF2 inst2,
    ModelF3 inst3,
    ModelT instT
}

然后他们在视图< - >之间传递它控制器有两种方式。

但是我想在不使用viewmodel的情况下以这种方式捕获我的模型:

class MyController {
    ActionResult Index() {
        return new View(modelF1Instance, modelF2Instance, modelF3Instance, modelTInstance);
    }

    ActionResult Form1Action(ModelF1 inst1, ModelT instT) {
        // process models after a form1 submit
    }

    ActionResult Form2Action(ModelF2 inst2, ModelT instT) {
        // process models after a form2 submit
    }

    ActionResult Form3Action(ModelF3 inst3, ModelT instT) {
        // process models after a form3 submit
    }
}

如果不解析CustomBinder中的整个表单元素,这是否可行?

1 个答案:

答案 0 :(得分:1)

首先,您只能使用

将强类型视图模型发送回视图
return View(model);

View是基类上的方法,而不是要使用return new View(...实例化的类

然后问你真正的问题:是的,你可以这样做,但是使用包含不同表单项的顶级ViewModel在大多数用例中要容易得多。顶级容器ViewModel处理的主要问题是值持久性和服务器端验证以及往返之间的错误消息。

如果您只是担心创建顶级ViewModel容器会导致效率低下,那么请不要这样做。这比您可能必须采用的所有变通方法更有效,以便在没有顶级ViewModel的情况下使表现良好的表单工作。

下面有一些示例代码。下面的代码应该证明使用顶级ViewModel中包含的模型只是更简单和更整洁:有些形式故意不会往返一些状态。请注意HiddenForModelState.Clear的使用情况,这两种情况都与您尝试执行的操作有关,但即使这些情况也不会保留inst4.Name Form4Submit的值。探讨的各种方案是:

  1. 使用查询参数表示正在发布的表单
  2. 使用其他表单名称,但仍使用视图模型。
  3. 对表单使用仅重定向操作(发送新实例,只显示部分视图模型)
  4. 使用上述
  5. 的混合物
    public class TestController : Controller
    {
        //
        // GET: /Test/
        [System.Web.Mvc.HttpGet]
        public ActionResult Index(string msg = null)
        {
            var model = new MyViewModel
            {
                Inst1 = new ModelF1 { Name = "Name of F1" },
                Inst2 = new ModelF2 (),
                InstT = new ModelT {Name = "Name of T"},
                PostNumber = 0,
                Message = msg
            };
            return View(model);
        }
    
        [System.Web.Mvc.HttpPost]
        public ActionResult Index(MyViewModel model, int option = 1)
        {
            // process models after a form1/2 submit
            model.Message = "You posted " + 
                ((option == 1) ? model.Inst1.Name : model.Inst2.Name)
                + " to Index for "
                + ((option == 1) ? "inst1" : "inst2");
            model.PostNumber ++;
            //  This, and the hiddenFor are required to allow us to update the PostNumber each time
            ModelState.Clear();
            return View(model);
        }
    
        [System.Web.Mvc.HttpPost]
        public ActionResult Form2Submit(MyViewModel model)
        {
            // process models after a form2 submit
            model.Message = "You posted " + model.Inst2.Name + " to Form2Submit";
            model.PostNumber++;
            ModelState.Clear();
            return View("Index", model);
        }
    
        [System.Web.Mvc.HttpPost]
        public ActionResult Form3Submit(ModelF3 inst3, ModelT instT)
        {
            // process models after a form3 submit
            var n = instT.Name;
            var msg = "You posted " + inst3.Name + ", " + n + " to Form3Submit";
            //  We no longer have access to pass information back to the view, so lets redirect
            return RedirectToAction("Index", new { msg = msg });
        }
    
        [System.Web.Mvc.HttpPost]
        public ActionResult Form4Submit(ModelF4 inst4, MyViewModel model)
        {
            // process models after a form4 submit
            var n = model.InstT.Name;
            model.Message = "You posted " + inst4.Name + ", " + n + " to Form4Submit";
            model.PostNumber++;
            ModelState.Clear();
            return View("Index", model);
        }
    
        public class MyViewModel
        {
            public int PostNumber { get; set; }
            public string Message { get; set; }
            public ModelF1 Inst1 { get; set; }
            public ModelF2 Inst2 { get; set; }
            public ModelT InstT { get; set; }
        }
    
        public class ModelBase { public string Name { get; set; } }
    
        public class ModelF1 : ModelBase {}
        public class ModelF2 : ModelBase { }
        public class ModelF3 : ModelBase { }
        public class ModelF4 : ModelBase { }
        public class ModelT : ModelBase { }
    }
    

    然后是多表格视图:

    @using MyWebSite.Controllers;
    @model TestController.MyViewModel
    @{
        Layout = null;
    }
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>Index</title>
    </head>
        <body>
            <p>
                @Html.Raw(Model.PostNumber) : @Html.Raw(Model.Message)
            </p>
            <p>
                @Html.LabelFor(m => Model.InstT) : <br />
                @Html.DisplayFor(m => Model.InstT)
            </p> 
            <div>
                <p>Default form submit</p>
                @using (Html.BeginForm())
                {
                    <div>
                        @Html.HiddenFor(m => m.PostNumber)
                        @Html.LabelFor(m => Model.Inst1.Name)
                        @Html.TextBoxFor(m => Model.Inst1.Name)
                    </div>
                    <input type="submit" value="Submit Index" />
                }
            </div>
            <div>
                <p>Use a parameter to denote the form being posted</p>
                @using (Html.BeginForm("Index", "Test", new { option = 2 }))
                {
                    <div>
                        @* Omitting these will not persist them between trips
                        @Html.HiddenFor(m => Model.Inst1.Name)
                        @Html.HiddenFor(m => Model.InstT.Name)*@
                        @Html.HiddenFor(m => m.PostNumber)
                        @Html.LabelFor(m => Model.Inst2.Name)
                        @Html.TextBoxFor(m => Model.Inst2.Name)
                    </div>
                    <input type="submit" value="Submit with option parameter" />
                }
            </div>
            <div>
                <p>Use a different form name, but still use the ViewModel</p>
                @using (Html.BeginForm("Form2Submit", "Test"))
                {
                    <div>
                        @Html.HiddenFor(m => Model.Inst1.Name)
                        @Html.HiddenFor(m => Model.InstT.Name)
                        @Html.HiddenFor(m => m.PostNumber)
                        @Html.LabelFor(m => Model.Inst2.Name)
                        @Html.TextBoxFor(m => Model.Inst2.Name)
                    </div>
                    <input type="submit" value="Submit F2" />
                }
            </div>
            <div>
                <p>Submit with a redirect, and no ViewModel usage.</p>
                @using (Html.BeginForm("Form3Submit", "Test"))
                {
                    var inst3 = new TestController.ModelF3();
                    <div>
                        @Html.HiddenFor(m => Model.InstT.Name)
                        @Html.LabelFor(m => inst3.Name)
                        @Html.TextBoxFor(m => inst3.Name)
                    </div>
                    <input type="submit" value="Submit F3" />
                }
            </div>
            <div>
                <p>Submit with a new class, and the ViewModel as well.</p>
                @using (Html.BeginForm("Form4Submit", "Test"))
                {
                    var inst4 = new TestController.ModelF4();
                    <div>
                        @Html.HiddenFor(m => Model.Message)
                        @Html.HiddenFor(m => Model.PostNumber)
                        @Html.HiddenFor(m => Model.Inst1.Name)
                        @Html.HiddenFor(m => Model.Inst2.Name)
                        @Html.HiddenFor(m => Model.InstT.Name)
                        @Html.LabelFor(m => inst4.Name)
                        @Html.TextBoxFor(m => inst4.Name)
                    </div>
                    <input type="submit" value="Submit F4" />
                }
            </div>
    
        </body>
    </html>