简体   繁体   中英

Whats the Appropriate Method to pass 2 different Models to the Controller from a View in MVC4

I am using MVC4 with SimpleMembership. MVC has setup my UserProfile table and I've modified the Registration View to accomodate my layout. Everything worked great until I determined that I wanted to include some additional information from my user that best fit within the context of another already existing Model.

Typically my approach to passing more than one Model from my View to the Controller has been to employ Tuples. Historically this has worked out great for me and I've never had any real issues until now.

My Registration Form resembles this:

@model Tuple<MyNamespace.Models.RegisterModel,MyNamespace.Models.MembershipDetail>

@using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl })) {
@Html.AntiForgeryToken()     
    <table style="border-collapse: collapse; border-spacing:0px; border-width:0px; margin: 0px; width:100px; padding: 0 0 0 0px; background-color:#2e2e2e;">
        <tr>
           <td>
              Profile Name
           </td>
           <td>
              @Html.TextBoxFor(m => m.Item2.ProfileName)
           </td>
        </tr> 
        <tr>
           <td>
              @Html.LabelFor(m => m.Item1.UserName)
           </td>
           <td>
              @Html.TextBoxFor(m => m.Item1.UserName)
           </td>
        </tr>
        <tr>
           <td>
              @Html.LabelFor(m => m.Item1.Password)
           </td>
           <td>
              @Html.PasswordFor(m => m.Item1.Password)
           </td>
        </tr>
        <tr>
           <td>
              @Html.LabelFor(m => m.Item1.ConfirmPassword)
           </td>
           <td>
              @Html.PasswordFor(m => m.Item1.ConfirmPassword)
           </td>
        </tr>                               
        <tr>
           <td>
              <button type="submit" id="btnSubmitForm" value="Register">Register</button>
           </td>
        </tr>
     </table>
     @Html.Partial("_ValidationSummary",ViewData.ModelState)                             
   }    

The Register Method of my Controller is similar to this:

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Register(RegisterModel model, MembershipDetail member)
    {
        if (ModelState.IsValid)
        {
            try
             {
                // Do something with the data
             }
            catch(MembershipCreateUserException e)
            {
               ModelState.AddModelError("",ErrorCodeToString(e.StatusCode));
            }
        }
        return View(model);
    }

Whenever I debug this code after the click of the Submit button, I note that both Models returned to the controller are empty. The contained fields are either null, 0 or Empty Strings and I cannot figure out why.

To add to this mystery, if I remove the tuple from the top of the view and re-assign it as a single Model as such:

@model MyNamespace.Models.RegisterModel

And replace the tuple references from the code (egmItem1.Property, m.Item2.Property) with the 1 Model inferred reference (m.Property and etc); then the data passed in from the TextBox helpers is appropriately assigned to the model and the model is populated when I debug the code.

Now, I know I could simply add a few other fields to the UserProfile table and use them in my Model to alleviate the Tuple altogether but then I am duplicating data in my schema which is not an ideal solution. And keeping in mind that though my sample code provided here only contains 1 element of the 2nd Model, it will actually be more than 1.

So why don't the Models get populated when using the Tuple and is there a more appropriate way to go about solving this problem? Or can I not mix Model data on a single form submission???

First of all , it is not a good practice to use Tuple as model, because it is hard to read the code. You should write page's own viewmodels for that. For this example like :

 public class SomeViewModel{
         public RegisterModel RegisterModel { get; set;}
         public MembershipDetail MembershipDetail {get; set;}
 } 

it is easy to implement and will work as you want.

However if you want to use Tuples on the view as model.

You should get Your post action like

   public ActionResult Register(Tuple<RegisterModel,MembershipDetail> model)
   {
     .... // do something..
   }

Because the form html will be created like :

  <input name="Item1.ProfileName" />  

You can see , it will try to send Item1.Profile name which is not supported by default model binder to convert it to your own classes.. it will expect a Tuple or a class like :.

   public class TheClass {
    public RegisterModel Item1 {get; set;}
    public MemberhipDetail Item2 {get; set;}
 }

which basically just like the viewmodel

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM