2014-05-13 10 views
6

Sto passando un ViewModel indietro dalla mia vista al controller tramite un modulo HttpPost. Tuttavia, i valori restituiti sono sempre NULL.MVC View ViewModel HttpPost il valore restituito è sempre NULL

ViewModel

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

    public SelectList Counties { get; set; } 
} 

società Class Modello

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; } 
} 

companyAddress Classe Modello

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 (ottenere):

// 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); 
    } 

View (creare)

@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> 
} 

Qualcuno può offrire qualche consiglio da perché i miei valori ViewModel restituiti sono NULL quando vengono compilati tutti i campi?

Ho controllato il browser Google Chrome utilizzando la funzione Record di rete e tutti i valori SONO registrati in formato JSON.

Molte grazie.

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

Ecco parte di quello che posso vedere dal Chrome Rete Google monitorare

Company.Name:ABC123 Company.CreatedDate: 2014/05/13 00:00:00 ....

quindi è sicuramente viene restituito.

+0

Abbastanza divertente, qualcuno ha avuto un problema simile con distacco posteriore "Company.Name". Per divertimenti, prova a rimuovere quel campo e vedere se registra ancora tutto null. http://stackoverflow.com/questions/780026/asp-net-mvc-model-binding-returning-null-values ​​ –

+1

CompanyAddressTypeId è scritto erroneamente. Inoltre, prova a definire il tuo modulo in questo modo: Html.BeginForm ("yourControllerNameHere", "Create", FormMethod.Post) – JB06

+0

Grazie a @ErikElkins tuttavia, rimuovere Company.Name dalla mia vista non ha fatto la differenza. Tutto torna ancora come NULL. – RobHurd

risposta

11

Sono stato in grado di riprodurre il problema ed è stato confuso perché so che il modello predefinito MVC Model Binder comprende tipi complessi. Ho rimosso la maggior parte del codice e ho appena provato a farlo con l'oggetto Company, che ancora non funzionava.Ho poi notato che nel vmCompanyAddress che il nome della classe era anche il nome della proprietà:

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

ho cambiato il nome della proprietà a qualcosa di diverso dal nome della classe ed è iniziato a lavorare:

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

Assolutamente perfetto! Molte grazie, apprezzo davvero che tu abbia esaminato questo per me. Di certo non farò più lo stesso errore. – RobHurd

+2

Sei il benvenuto. Sebbene il compilatore lo consenta, non sono un grande fan di nominare le mie proprietà come il nome della classe in cui sono definite. Osservo i nomi delle classi come parole riservate e quindi non dovrebbe esserci confusione quando si fa riferimento alla classe rispetto a un'istanza della classe. –

2

Non ho provato questo, ma ho avuto un sacco di problemi simili molto tempo fa che ho risolto con ModelBinder personalizzato: s che non consiglio.

Immagino che i dati non siano simili: {Azienda: {...}, Indirizzo: {...}}?

Penso che la soluzione sia avere MVC per capire la struttura dei dati utilizzando i modelli e EditorFor(). Vedi http://lostechies.com/jimmybogard/2011/09/07/building-forms-for-deep-view-model-graphs-in-asp-net-mvc/ per un buon esempio!

+0

Grazie per l'input. @MatthewAtCriticalCogni ha colpito l'unghia sulla testa. La mia proprietà ViewModel è stata nominata la stessa della mia classe, causando così un errore. – RobHurd

10

Abbiamo avuto lo stesso problema oggi. La risposta accettata in questa domanda è solo una soluzione sporca per il problema reale.

ClassName e PropertyName in un modello di modulo possono essere uguali, non esiste alcuna limitazione nel raccoglitore del modello. La limitazione è il parametro dell'azione nel controller. Non è necessario assegnare un nome al parametro come una proprietà di tipo complesso nel modello di modulo. Causa il raccoglitore tenterà di associare il valore del modulo HTTP POST di company a questo parametro nel controller. Non funzionerà per te, perché il raccoglitore tenta di associare i valori di un tipo Company al tipo CompanyAddress.

Per risolvere il problema, è sufficiente rinominare il parametro company in companyAddressModel o qualsiasi altra cosa che non sia una proprietà nella classe del modello.

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

modifica:

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

Vedi qui per maggiori informazioni sull'associazione modello: http://aspnetmvc.readthedocs.org/projects/mvc/en/latest/models/model-binding.html

MVC cercherà di associare i dati di richiesta all'azione parametri per nome. MVC cercherà i valori per ogni parametro utilizzando il nome del parametro e il nome delle sue proprietà modificabili pubbliche. [...] Oltre ai valori di percorso MVC associa i dati da varie parti della richiesta e lo fa in un ordine impostato. Di seguito è riportato un elenco delle fonti di dati nell'ordine in cui modello vincolante sguardi attraverso di loro:

  • Modulo valori: Questi sono i valori di forma che vanno nella richiesta HTTP utilizzando il metodo POST.
  • Valori di instradamento: l'insieme di valori di percorso forniti dal routing.
  • Stringhe di query: la parte della stringa di query dell'URI.

Un buon esempio da ASP.NET WebAPI documentation, che sta utilizzando la stessa tecnica:

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

Qui la proprietà di ProductId è mappato al parametro id nel controllore. Che funzionerà, perché nell'azione viene utilizzato lo stesso tipo di dati primitivi come nella classe del modello.

2

Verificare che ViewModel esponga le proprietà e non solo i campi.

Questo funziona:

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

Questo non lo fa:

public DAL.Models.Account acct;