2014-05-13 16 views
6

Ich übergebe ein ViewModel von meiner Ansicht an den Controller über ein Formular HttpPost. Die zurückgegebenen Werte sind jedoch immer NULL.MVC Ansicht ViewModel HttpPost Rückgabewert ist immer NULL

Ansichtsmodell

public class vmCompanyAddress 
{ 
    public StatelyTechAdmin.Models.Company Company { get; set; } 
    public StatelyTechAdmin.Models.CompanyAddress Address { get; set; } 

    public SelectList Counties { get; set; } 
} 

Firma Klasse Modell

public class Company 
{ 
    [Key] 
    public virtual long CompanyId { get; set; } 

    [Required] 
    [Display(Name = "Company Name")] 
    public virtual string Name { get; set; } 

    public virtual DateTime CreatedDate { get; set; } 

    public virtual IEnumerable<CompanyAddress> CompanyAddresses { get; set; } 
} 

company Klasse Modell

public class CompanyAddress 
{ 
    [Key] 
    public virtual long CompanyAddressId { get; set; } 

    [Required] 
    public virtual long CompanyId { get; set; } 

    [ForeignKey("CompanyId")] 
    public virtual Company Company { get; set; } 

    [Required] 
    public virtual int CopmanyAddressTypeId { get; set; } 

    [ForeignKey("CopmanyAddressTypeId")] 
    public virtual CompanyAddressType CompanyAddressType { get; set; } 

    [Display(Name = "Address 1")] 
    public virtual string Address1 { get; set; } 

    [Display(Name = "Address 2")] 
    public virtual string Address2 {get; set; } 

    [Display(Name = "Town")] 
    public virtual string Town { get; set; } 

    [Display(Name = "City")] 
    public virtual string City { get; set; } 

    [Required] 
    public virtual long CountyId { get; set; } 

    [ForeignKey("CountyId")] 
    [Display(Name = "County")] 
    public virtual County County { get; set; } 

    [Required] 
    [Display(Name = "Postal Code")] 
    public virtual string PostalCode { get; set; } 

    public virtual DateTime CreatedDate { get; set; } 
} 

Controller (erhalten):

// GET: /Company/Create 
    public ActionResult Create() 
    { 
     vmCompanyAddress vm = new vmCompanyAddress(); 
     vm.Counties = new SelectList(db.County, "CountyId", "Name", -1); 
     //vm.Address = new CompanyAddress(); 
     //vm.Company = new Company(); 

     return View(vm); 
    } 

Controller (post):

[HttpPost] 
    [ValidateAntiForgeryToken] 
    public ActionResult Create(vmCompanyAddress company) 
    { 
     if (ModelState.IsValid) 
     { 
      db.Companies.Add(company.Company); 

      //Amend Address Company & Address Type before save to DB 
      company.Address.CompanyId = company.Company.CompanyId; 
      company.Address.CopmanyAddressTypeId = 1; 

      db.CompanyAddress.Add(company.Address); 

      db.SaveChanges(); 
      return RedirectToAction("Index"); 
     } 

     return View(company); 
    } 

anzeigen (create)

@model StatelyTechAdmin.ViewModels.vmCompanyAddress 

@{ 
    ViewBag.Title = "Create"; 
} 

<h2>Create</h2> 

@using (Html.BeginForm()) { 
    @Html.AntiForgeryToken() 
    @Html.ValidationSummary(true) 

    <fieldset> 
     <legend>Company</legend> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.Company.Name) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.Company.Name) 
      @Html.ValidationMessageFor(model => model.Company.Name) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.Company.CreatedDate) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.Company.CreatedDate) 
      @Html.ValidationMessageFor(model => model.Company.CreatedDate) 
     </div> 


     @* Invoice Address *@ 
     <div class="editor-label"> 
      @Html.LabelFor(model => model.Address.Address1) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.Address.Address1) 
      @Html.ValidationMessageFor(model => model.Address.Address1) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.Address.Address2) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.Address.Address2) 
      @Html.ValidationMessageFor(model => model.Address.Address2) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.Address.Town) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.Address.Town) 
      @Html.ValidationMessageFor(model => model.Address.Town) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.Address.City) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.Address.City) 
      @Html.ValidationMessageFor(model => model.Address.City) 
     </div> 

     @*<div class="editor-label"> 
      @Html.LabelFor(model => model.Address.County) 
     </div> 
     <div class="editor-field"> 
      @Html.DropDownListFor(model => model.Address.CountyId, Model.Counties) 
     </div>*@ 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.Address.PostalCode) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.Address.PostalCode) 
      @Html.ValidationMessageFor(model => model.Address.PostalCode) 
     </div> 

     <p> 
      <input type="submit" value="Create" /> 
     </p> 
    </fieldset> 
} 

Kann jemand bieten bitte einen Rat zu Warum sind meine ViewModel-Werte NULL, wenn alle Felder ausgefüllt sind?

Ich habe in Google Chrome-Browser mit der Network Record-Funktion eingecheckt und alle Werte sind zurück im JSON-Format gebucht.

Vielen Dank.

------------ EDIT ---------------

Hier ist ein Teil von dem, was ich aus dem Google Chrome-Netzwerk sehen kann Monitor-

Company.Name:ABC123 Company.CreatedDate: 2014.05.13 00.00.00 ....

So ist es auf jeden Fall zurückgegeben werden.

+0

Lustig genug, jemand hat mit der Veröffentlichung zurück "Company.Name" ein ähnliches Problem hatte. Versuchen Sie, das Feld zu entfernen, um zu sehen, ob es immer noch null zurückgibt. http://stackoverflow.com/questions/780026/asp-net-mvc-model-binding-returning-null-values ​​ –

+1

CompanyAddressTypeId wird falsch geschrieben. Versuchen Sie auch, Ihr Formular wie folgt zu definieren - Html.BeginForm ("yourControllerNameHere", "Create", FormMethod.Post) – JB06

+0

Dank @ErikElkins jedoch entfernt das Entfernen von Company.Name aus meiner Ansicht keinen Unterschied. Alles wird immer noch als NULL zurückgeschrieben. – RobHurd

Antwort

11

Ich konnte Ihr Problem reproduzieren und war verwirrt, weil ich weiß, dass der Standard MVC Model Binder komplexe Typen versteht. Ich entfernte den Großteil des Codes und versuchte es nur mit dem Company-Objekt, das immer noch fehlschlug.Ich bemerkte dann, dass in vmCompanyAddress, dass der Name der Klasse auch der Name der Eigenschaft war:

public class vmCompanyAddress 
{ 
    public StatelyTechAdmin.Models.Company Company { get; set; } 

ich den Namen der Eigenschaft auf etwas anderes als den Klassennamen geändert und es anfing zu arbeiten:

public class vmCompanyAddress 
{ 
    public StatelyTechAdmin.Models.Company TheCompany { get; set; } 
+3

Absolut genau richtig! Vielen Dank, wirklich zu schätzen, dass Sie sich für mich damit beschäftigen. Ich werde sicherlich nicht wieder denselben Fehler machen. – RobHurd

+2

Sie sind herzlich willkommen. Obwohl der Compiler es erlaubt, bin ich kein großer Fan davon, meine Eigenschaften so zu benennen, wie der Klassenname, für den sie definiert sind. Ich sehe die Klassennamen als reservierte Wörter an und sollte daher keine Verwirrung bei der Referenzierung der Klasse gegenüber einer Instanz der Klasse hervorrufen. –

2

Ich habe das selbst nicht ausprobiert, aber hatte viele ähnliche Probleme vor langer Zeit, die ich mit benutzerdefinierten ModelBinder gelöst habe: s, die ich nicht empfehle.

Ich denke, Ihre Daten sehen nicht so aus: {Firma: {...}, Adresse: {...}}?

Ich denke, die Lösung ist MVC, die Struktur der Daten mit Vorlagen und EditorFor() zu verstehen. Ein gutes Beispiel finden Sie unter http://lostechies.com/jimmybogard/2011/09/07/building-forms-for-deep-view-model-graphs-in-asp-net-mvc/!

+0

Danke für die Eingabe. @MatthewAtCriticalCogni traf den Nagel auf den Kopf. Meine ViewModel-Eigenschaft wurde wie meine Klasse benannt, wodurch sie fehlschlägt. – RobHurd

10

Wir hatten das gleiche Problem heute. Die akzeptierte Antwort in dieser Frage ist nur ein schmutziger Workaround für das eigentliche Problem.

ClassName und PropertyName in einem Formularmodell können identisch sein, es gibt keine Einschränkung im Modellbinder. Die Begrenzung ist der Parameter der Aktion in Ihrem Controller. Sie dürfen den Parameter nicht als eine Eigenschaft mit komplexem Typ in Ihrem Formularmodell benennen. Der Binder versucht, den HTTP-POST-Formularwert company an diesen Parameter in Ihrem Controller zu binden. Es funktioniert nicht für Sie, da der Ordner versucht, die Werte eines Company-Typs an CompanyAddress Typ zu binden.

Um Ihr Problem zu beheben, müssen Sie einfach den Parameter company in companyAddressModel umbenennen - oder alles, was keine Eigenschaft in Ihrer Modellklasse ist.

[HttpPost] 
[ValidateAntiForgeryToken] 
public ActionResult Create(CompanyAddress company) 

Änderung:

[HttpPost] 
[ValidateAntiForgeryToken] 
public ActionResult Create(CompanyAddress companyAddressModel) 

hier für weitere Informationen über Modell Bindung: http://aspnetmvc.readthedocs.org/projects/mvc/en/latest/models/model-binding.html

MVC werden versuchen, auf Anfrage Daten an die Aktion Parameter zu binden Namen. MVC sucht nach Werten für jeden Parameter mit dem Parameternamen und den Namen seiner öffentlichen einstellbaren Eigenschaften. [...] Zusätzlich zu den Routenwerten wird MVC Daten von verschiedenen Teilen der Anfrage binden und es tut so in eine festgelegte Reihenfolge. Im Folgenden finden Sie eine Liste der Datenquellen in der Reihenfolge, in Modell sieht durch sie verbindlich:

  • Formularwerte: Formularwerte Dies sind, die die POST-Methode in der HTTP-Anforderung gehen mit.
  • Routenwerte: Der Satz von Routenwerten, die vom Routing bereitgestellt werden.
  • Abfragezeichenfolgen: Der Abfragezeichenfolgeteil des URI.

Ein gutes Beispiel von ASP.NET WebAPI documentation, die die gleiche Technik verwendet:

HttpResponseMessage Put(int id, Product item) { ... } 

Hier wird die Eigenschaft von IdProduct zum id Parameter in der Steuerung zugeordnet ist. Das wird funktionieren, weil in der Aktion der gleiche primitive Datentyp wie in der Modellklasse verwendet wird.

2

Stellen Sie sicher, dass Ihr ViewModel Eigenschaften und nicht nur Felder offen legt.

Dies funktioniert:

public DAL.Models.Account acct {get;set;} 

Dies gilt nicht:

public DAL.Models.Account acct;