Dire che ho un modello di prodotto, il modello di prodotto ha una proprietà di ProductSubType (abstract) e abbiamo due implementazioni concrete Shirt and Pants.MVC 3 Modello che associa un sottotipo (Classe astratta o interfaccia)
Qui è la fonte:
public class Product
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public decimal? Price { get; set; }
[Required]
public int? ProductType { get; set; }
public ProductTypeBase SubProduct { get; set; }
}
public abstract class ProductTypeBase { }
public class Shirt : ProductTypeBase
{
[Required]
public string Color { get; set; }
public bool HasSleeves { get; set; }
}
public class Pants : ProductTypeBase
{
[Required]
public string Color { get; set; }
[Required]
public string Size { get; set; }
}
Nel mio UI, l'utente ha un menu a discesa, è possibile selezionare il tipo di prodotto e gli elementi di ingresso vengono visualizzati in base al tipo di prodotto giusto. Ho capito tutto questo (usando un ajax get on dropdown change, restituisci un partial/editor template e re-setup la validazione jquery di conseguenza).
Successivamente ho creato un raccoglitore modello personalizzato per ProductTypeBase.
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ProductTypeBase subType = null;
var productType = (int)bindingContext.ValueProvider.GetValue("ProductType").ConvertTo(typeof(int));
if (productType == 1)
{
var shirt = new Shirt();
shirt.Color = (string)bindingContext.ValueProvider.GetValue("SubProduct.Color").ConvertTo(typeof(string));
shirt.HasSleeves = (bool)bindingContext.ValueProvider.GetValue("SubProduct.HasSleeves").ConvertTo(typeof(bool));
subType = shirt;
}
else if (productType == 2)
{
var pants = new Pants();
pants.Size = (string)bindingContext.ValueProvider.GetValue("SubProduct.Size").ConvertTo(typeof(string));
pants.Color = (string)bindingContext.ValueProvider.GetValue("SubProduct.Color").ConvertTo(typeof(string));
subType = pants;
}
return subType;
}
}
Questa lega i valori correttamente e funziona per la maggior parte, tranne che perdo la validazione lato server. Così su una sensazione che sto facendo questo in modo non corretto ho fatto un po 'di ricerca e sono imbattuto in questa risposta da Darin Dimitrov:
ASP.NET MVC 2 - Binding To Abstract Model
Così ho acceso il modello legante per ignorare solo CreateModel, ma ora non lo fa legare i valori.
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
ProductTypeBase subType = null;
var productType = (int)bindingContext.ValueProvider.GetValue("ProductType").ConvertTo(typeof(int));
if (productType == 1)
{
subType = new Shirt();
}
else if (productType == 2)
{
subType = new Pants();
}
return subType;
}
passo se il MVC 3 src, sembra che in BindProperties, i GetFilteredModelProperties restituisce un risultato vuoto, e penso che è perché il modello BindingContext è impostato su ProductTypeBase che non ha alcuna proprietà.
Qualcuno può individuare ciò che sto facendo male? Non sembra che dovrebbe essere così difficile. Sono sicuro che mi manca qualcosa di semplice ... Ho un'altra alternativa in mente invece di avere una proprietà SubProduct nel modello Product per avere solo proprietà separate per Shirt and Pants. Questi sono solo i modelli View/Form quindi penso che funzionerebbe, ma vorrei che l'approccio attuale funzioni se non altro per capire cosa sta succedendo ...
Grazie per qualsiasi aiuto!
Aggiornamento:
Non ho fatto in chiaro, ma il modello personalizzato legante ho aggiunto, eredita dalla DefaultModelBinder
risposta
impostazione ModelMetadata e modello era il pezzo mancante. Grazie Manas!
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType.Equals(typeof(ProductTypeBase))) {
Type instantiationType = null;
var productType = (int)bindingContext.ValueProvider.GetValue("ProductType").ConvertTo(typeof(int));
if (productType == 1) {
instantiationType = typeof(Shirt);
}
else if (productType == 2) {
instantiationType = typeof(Pants);
}
var obj = Activator.CreateInstance(instantiationType);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, instantiationType);
bindingContext.ModelMetadata.Model = obj;
return obj;
}
return base.CreateModel(controllerContext, bindingContext, modelType);
}
Perfetto, l'impostazione di ModelMetaData e Model in create era il pezzo mancante, grazie! –
Ho avuto un problema molto simile con un tipo ereditato e derivato e il suddetto modello di codifica del modello ha fatto il trucco. Saluti! –
Grazie! Trascorro quasi tre giorni provando cose diverse per ricorrere infine a questa soluzione, anche se il mio problema era un po 'diverso. –