Вот так я бы начал подходить к вопросу.Привязка пользовательской модели будет довольно легко построить на основе свойства FormKey (которое может определяться индексом и / или меткой, в зависимости от).
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; }
}
Кроме того, я заметил, что один из комментариев ниже имелотфильтрованная модель связующей системы.Джимми Богард из Automapper сделал очень полезную публикацию об этом методе в http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx, а затем исправил в http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/19/a-better-model-binder-addendum.aspx.Это очень помогло мне в создании пользовательских связывателей моделей.
Обновление
Я понял, что неверно истолковал вопрос и что он специально спрашивал, как обрабатывать публикацииформы "с переменным количеством полей ввода, которые представляют разные типы данных".Я думаю, что лучший способ сделать это - использовать структуру, аналогичную описанной выше, но использовать составной паттерн .По сути, вам нужно будет создать интерфейс, подобный IFormComponent
, и реализовать его для каждого типа данных, который будет представлен.Я написал и прокомментировал пример интерфейса, чтобы объяснить, как это можно сделать:
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);
}
Я предполагаю, что к пользовательским формам можно получить доступ через некоторый вид идентификатора, который может содержаться в качестве параметра формы.При таком допущении связыватель модели и поставщик могут выглядеть примерно так.
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.");
}
}
Очевидно, что весь код здесь только для того, чтобы понять суть, и его необходимо будет завершить и очистить для фактического использования.,Кроме того, даже если это будет выполнено, это будет связано только с реализацией интерфейса IForm, а не со строго типизированным бизнес-объектом.(Преобразование его в словарь и создание строго типизированного прокси с использованием Castle DictionaryAdapter не будет огромным шагом, но, поскольку ваши пользователи динамически создают формы на сайте, в вашем решении, вероятно, нет строго типизированной модели иэто не имеет значения).Надеюсь, это поможет больше.