2010-02-16 16 views
7

Ho una situazione in qualche modo normale in cui è necessario associare i moduli del modulo a un modello di "ordine". Questo modello ha un paio di livelli di informazione ad esso:DefaultModelBinder Problema con livelli nidificati + altri raccoglitori

Order.Billing.FirstName 
Order.Billing.Address.City 
Order.Billing.Address.Country 

Utilizzando la DefaultModelBinder, se io inserisco un modulo per un'azione che prende questo Modello di ordine come il parametro, i seguenti campi JustWork (TM):

<%=Html.TextBox("Billing.FirstName")%> 
<%=Html.TextBox("Billing.Address.City")%> 

Questo campo non è così:

<%=Html.TextBox("Billing.Address.Country")%> 

la ruga che ho è con la proprietà di paese. Nel nostro caso, Address.Country restituisce un'istanza di classe Paese (logica ISO2/3/Nome/Codice). Non è una stringa. Non sorprende che non funzioni di default.

Il mio primo pensiero è stato quello di creare un CountryModelBinder (eredita DefaultModelBinder) e ModelBinders.Binders. Aggiungilo al tipo di Paese. Quando lo faccio, CountryModelBinder non viene mai chiamato nello scenerio sopra.

Il mio secondo pensiero è stato quello di creare un AddressModelBinder (ereditare DefaultModelBinder) e collegarlo al nostro tipo di indirizzo. Mentre viene chiamato, la chiamata SetProperty per "Paese" ha un valore vuoto, anche se il modulo ha pubblicato un campo chiamato "Billing.Address.Country".

Dopo alcuni tentativi, sembra che il comportamento del collegamento del modello chiama CreateModel solo quando il modello è la classe di livello superiore desiderata dall'azione e tutti gli altri raccoglitori hanno BindPropery/SetProperty chiamato per proprietà figlio.

In altre parole, se creo i raccoglitori modello per Ordine, Indirizzo ordine (Fatturazione), Indirizzo e Paese. Per l'azione che richiede un ordine, viene chiamato solo OrderModelBinder.CreateModel. ORDERAddress e Address.BindProperty/SetProperty vengono chiamati per alcune cose e talvolta l'argomento del valore SetProperty è vuoto quando è stato pubblicato in un nome che corrisponde agli altri mapping delle proprietà del campo.

È abbastanza semplice aggiungere codice a OrderModelBinder per estrarre Billing.Address.Country da Request.Form. Ma ho più modelli che usano Address e fare tutto ciò che sembra rotto.

Cosa mi manca qui? C'è un modo per far chiamare effettivamente CountryModelBinder in questo caso? Penso che CountryModelBinder debba essere chiamato quando Billing.Address.Country è mappato alla proprietà Country del Raccoglitore indirizzi.

+0

questo potrebbe aiutarti: http://stackoverflow.com/questions/2462506/model-binding-with-nested-child-models-and-partialviews-in-asp-net-mvc – Will

+0

Sto avendo lo stesso problema con una struttura del modello annidata, mi sembra che le proprietà al livello più alto e un livello inferiore siano vincolate, ma qualsiasi cosa più bassa sembra essere ignorata. È questo il comportamento normale del legatore del modello? Sembra un comportamento piuttosto arbitrario. – UpTheCreek

risposta

0

Ho provato a fare quello che hai fatto qui, apparentemente su MVC3 funziona davvero se fornisco un raccoglitore modello per quel tipo.

Questo è solo una prova di concetto per dimostrare che funziona, e non dovrebbe essere considerata anche vicino al codice di livello di produzione:

Modelli:

public class SimpleModel 
    { 
     public string Value { get; set; } 
     public int Other { get; set; } 
    } 

    public class ComplexModel 
    { 
     public SimpleModel Complexity {get;set;} 
     public string StrVal { get; set; } 
    } 

alcuni legante:

public class MBinder : IModelBinder 
     { 
      public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
      { 
       if (bindingContext.ModelType == typeof(SimpleModel)) 
       { 
        var simpleModel= new SimpleModel(); 
        simpleModel.Other = 1; 
        simpleModel.Value = controllerContext.HttpContext.Request.Form["Complexity"]; 

        return cm; 
       } 
       return null; 
      } 
     } 

in ASax globale:

ModelBinders.Binders.Add(typeof (SimpleModel), new MBinder()); 

codice Vista:

@model ComplexModel 

    @using (Html.BeginForm()) 
{ 
    <fieldset> 
     @Html.LabelFor(x => x.Complexity) 
     @Html.TextBoxFor(x => x.Complexity) 
    </fieldset> 

    <fieldset> 
     @Html.LabelFor(x => x.StrVal) 
     <br /> 
     @Html.EditorFor(x => x.StrVal) 
    </fieldset> 
    <input type="submit" /> 
} 

Controller:

public ActionResult Index() 
     { 
      return View(); 
     } 

     [HttpPost] 
     public ActionResult Index(ComplexModel model) 
     { 
      return RedirectToAction("Index"); 

     } 

BTW in MVC 3 una soluzione migliore sarebbe quella di utilizzare l'interfaccia IModelBinderProvider, ma ho solo voluto mostrare qualcosa che avrebbe funzionato.

Problemi correlati