Así es como comenzaría a abordar el problema. Un encuadernador de modelo personalizado sería bastante fácil de construir en función de la propiedad FormKey (que podría estar determinada por el índice y/o la etiqueta, según corresponda).
public class CustomFormModel
{
public string FormId { get; set; }
public string Label { get; set; }
public CustomFieldModel[] Fields { get; set; }
}
public class CustomFieldModel
{
public DataType DateType { get; set; } // System.ComponentModel.DataAnnotations
public string FormKey { get; set; }
public string Label { get; set; }
public object Value { get; set; }
}
public class CustomFieldModel<T> : CustomFieldModel
{
public new T Value { get; set; }
}
Además, noté que uno de los comentarios a continuación tenía un sistema de encuadernación modelo filtrado. Jimmy Bogard de Automapper hizo una publicación muy útil acerca de este método al http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx, y luego la revisó en http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/19/a-better-model-binder-addendum.aspx. Ha sido muy útil para mí en la construcción de carpetas de modelo personalizado.
actualización
me di cuenta de que la pregunta mal interpretado, y que él estaba pidiendo específicamente cómo manejar publicación de la forma "con un número variable de campos de entrada que representan diferentes tipos de datos". Creo que la mejor manera de hacerlo es usar una estructura similar a la anterior pero aprovechar el Composite Pattern. Básicamente, deberá crear una interfaz como IFormComponent
e implementarla para cada tipo de datos que se representará. He escrito y comentado un ejemplo de interfaz para ayudar a explicar cómo esto se lograría:
public interface IFormComponent
{
// the id on the html form field. In the case of a composite Id, that doesn't have a corresponding
// field you should still use something consistent, since it will be helpful for model binding
// (For example, a CompositeDateField appearing as the third field in the form should have an id
// something like "frmId_3_date" and its child fields would be "frmId_3_date_day", "frmId_3_date_month",
// and "frmId_3_date_year".
string FieldId { get; }
// the human readable field label
string Label { get; }
// some functionality may require knowledge of the
// Parent component. For example, a DayField with a value of "30"
// would need to ask its Parent, a CompositeDateField
// for its MonthField's value in order to validate
// that the month is not "February"
IFormComponent Parent { get; }
// Gets any child components or null if the
// component is a leaf component (has no children).
IList<IFormComponent> GetChildren();
// For leaf components, this method should accept the AttemptedValue from the value provider
// during Model Binding, and create the appropriate value.
// For composites, the input should be delimited in someway, and this method should parse the
// string to create the child components.
void BindTo(string value);
// This method should parse the Children or Underlying value to the
// default used by your business models. (e.g. a CompositeDateField would
// return a DateTime. You can get type safety by creating a FormComponent<TValue>
// which would help to avoid issues in binding.
object GetValue();
// This method would render the field to the http response stream.
// This makes it easy to render the forms simply by looping through
// the array. Implementations could extend this for using an injected
// formatting
void Render(TextWriter writer);
}
Estoy asumiendo que los formularios personalizados se puede acceder a través de algún tipo de identificación que puede estar contenido como un parámetro de forma. Con esa suposición, el encuadernador modelo y el proveedor podrían verse más o menos así.
public interface IForm : IFormComponent
{
Guid FormId { get; }
void Add(IFormComponent component);
}
public interface IFormRepository
{
IForm GetForm(Guid id);
}
public class CustomFormModelBinder : IModelBinder
{
private readonly IFormRepository _repository;
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult result;
if(bindingContext.ValueProvider.TryGetValue("_customFormId", out result))
{
var form = _repository.GetForm(new Guid(result.AttemptedValue));
var fields = form.GetChildren();
// loop through the fields and bind their values
return form;
}
throw new Exception("Form ID not found.");
}
}
Obviamente, todo el código aquí es sólo para conseguir el punto a través, y tendría que ser completado y limpiado por el uso real. Además, incluso si se completa, esto solo se vincularía a una implementación de la interfaz IForm, no a un objeto comercial fuertemente tipado.(No sería un gran paso convertirlo a un diccionario y construir un proxy fuertemente tipado usando Castle DictionaryAdapter, pero como sus usuarios están creando dinámicamente los formularios en el sitio, probablemente no haya un modelo fuertemente tipado en su solución y esto es irrelevante). Espero que esto ayude más.
Gracias por los comentarios, muy perspicaces. – DanP