Supongo que tengo un modelo de Producto, el modelo de Producto tiene una propiedad de ProductSubType (resumen) y tenemos dos implementaciones concretas Camisa y Pantalones.Modelo MVC 3 Cómo vincular un subtipo (clase o interfaz abstracta)
Aquí está la fuente:
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; }
}
En mi interfaz de usuario, el usuario tiene una lista desplegable, se puede seleccionar el tipo de producto y los elementos de entrada se muestran según el tipo de producto adecuado. Tengo todo esto resuelto (usando un cambio de menú desplegable ajax get, devuelvo una plantilla parcial/editor y vuelvo a configurar la validación de jquery en consecuencia).
A continuación, creé un archivador de modelo personalizado para 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;
}
}
Esto une los valores correctamente y funciona en su mayor parte, excepto que pierdo la validación del lado del servidor. Así que en el presentimiento de que estoy haciendo esto de forma incorrecta He hecho un poco más la búsqueda y se encontró con esta respuesta de Darin Dimitrov:
ASP.NET MVC 2 - Binding To Abstract Model
así que cambié el modelo de aglutinante sólo anular CreateModel, pero ahora no lo hace enlazar los valores.
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;
}
Stepping aunque el src MVC 3, parece que en BindProperties, los GetFilteredModelProperties devuelve un resultado vacío, y creo que es porque el modelo BindingContext se establece en ProductTypeBase que no tiene ninguna propiedad.
¿Alguien puede detectar lo que estoy haciendo mal? Esto no parece ser así de difícil. Estoy seguro de que me falta algo simple ... Tengo otra alternativa en mente en lugar de tener una propiedad SubProduct en el modelo de Producto para tener propiedades separadas para Camisa y Pantalones. Estos son solo modelos de Vista/Forma, así que creo que funcionaría, pero me gustaría que el enfoque actual funcione para entender lo que está pasando ...
¡Gracias por cualquier ayuda!
Actualización:
que no quede claro, pero el modelo personalizado ligante añadí, hereda de la DefaultModelBinder
respuesta
ajuste de ModelMetadata y modelo fue la pieza que falta. Gracias 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);
}
Perfecto, configurar el ModelMetaData y el modelo en crear fue la pieza que falta, ¡gracias! –
Tuve un problema muy similar con un tipo heredado y derivado, y el código de encuadernador anterior funcionó. ¡Aclamaciones! –
Gracias! Pasé casi tres días intentando algo diferente para finalmente recurrir a esta solución, aunque mi problema era algo diferente. –