W ten sposób zacznę podejść do problemu. Niestandardowy segregator modelu można byłoby łatwo zbudować na podstawie właściwości FormKey (która może być określona przez indeks i/lub etykietę w zależności od).
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; }
}
Zauważyłem również, że jeden z poniższych komentarzy miał filtrowany system segregatorów. Jimmy Bogard z Automapper zrobił bardzo pomocny post na temat tej metody pod adresem http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx, a następnie poprawił ją w wersji http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/19/a-better-model-binder-addendum.aspx. Bardzo mi pomogło w tworzeniu niestandardowych segregatorów modeli.
Aktualizacja
zdałem sobie sprawę, że błędnie zinterpretował pytanie, i że został specjalnie pytając, jak radzić delegowania postaci „ze zmienną liczbą pól wejściowych, które reprezentują różne typy danych.” Myślę, że najlepszym sposobem na to jest użycie struktury podobnej do powyższej, ale skorzystaj z Composite Pattern. Zasadniczo, będziesz musiał stworzyć interfejs taki jak IFormComponent
i zaimplementować go dla każdego typu danych, który byłby reprezentowany. Pisałem i skomentował interfejs przykład pomóc wyjaśnić, w jaki sposób to osiągnąć:
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);
}
jestem przy założeniu, że można uzyskać poprzez niestandardowe formy jakiegoś id, które mogą być zawarte jako parametr formularza. Przy takim założeniu model spoiwa i dostawca mogą wyglądać mniej więcej tak.
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.");
}
}
Oczywiście, cały kod jest tu po prostu, aby uzyskać punkt całej, i musiałyby zostać zakończone i oczyścić do faktycznego wykorzystania. Ponadto, nawet jeśli jest to zakończone, wiązałoby się tylko z implementacją interfejsu IForm, a nie z silnie wpisanym obiektem biznesowym.(Nie byłoby wielkim krokiem do przekonwertowania go do słownika i zbudowania silnie typowanego proxy przy użyciu Castle DictionaryAdapter, ale ponieważ Twoi użytkownicy dynamicznie tworzą formularze w witrynie, prawdopodobnie nie ma silnie wpisanego modelu w twoim rozwiązaniu i to nie ma znaczenia). Mam nadzieję, że to pomaga bardziej.
Dzięki za komentarze, bardzo wnikliwe. – DanP