Привязка модели в веб-формах ASP.NET

В течение нескольких лет я занимался разработкой веб-форм ASP.NET. Меня баловала проприетарная библиотека, которая позволяла мне делать такие вещи, как:

    UpdateToObject(ControlsCollection, obj)
    UpdateFromObject(ControlsCollection, obj)

Концептуально код делал что-то очень похожее на то, что делает MVC Model Binder, т. е. учитывая отправленные значения формы в качестве входных данных, он заполнял пользовательский объект. По сути, это освободило разработчика от написания обезьяньего кода, такого как

employee.Name = txtName.Text;
employee.DOB = DateTime.Parse(txtDOB.Text);

и так далее..

Теперь эта проприетарная библиотека недоступна в новом проекте, в котором я участвую, и это проект веб-форм. Поэтому мне интересно, есть ли способ использовать System.Web.Mvc.DefaultModelBinder в контексте веб-форм. Цель состоит в том, чтобы обеспечить простое и легкое заполнение элементов управления из объектов предметной области и обратно, в идеале с учетом аннотаций проверки. Если это невозможно, может ли кто-нибудь указать мне решение с открытым исходным кодом для удовлетворения этой потребности. Мне действительно не хочется переписывать такой код.

Заранее спасибо.


person Sherlock    schedule 09.09.2010    source источник


Ответы (3)


Шерлок, вы столкнетесь с некоторыми проблемами, пытаясь использовать ModelBinder из MVC, поскольку они полагаются на ControllerContext.

Я ответил на аналогичный вопрос ранее ChangeType, Convert - преобразование из одного типа в другой но это действительно то, что вам нужно.

Посмотрите этот пост в моем блоге ChangeType — изменение типа переменной в C#

По сути, вы получаете единственный метод с именем ChangeType<T>, который возвращает значение искомого параметра в строго типизированном виде или значение по умолчанию, если параметр не существует.

Теперь, что касается пользовательских классов (в основном классов типа DTO), если вы не возражаете против использования отражения, у меня есть решение, которое также будет обрабатывать большинство пользовательских классов. Класс DtoBinder, упомянутый в конце, будет хорошо работать.

По сути, последние 3 листинга кода содержат весь код, который вам понадобится для обработки почти всех ваших потребностей в типичном сценарии веб-приложения. Кроме того, он расширяемый, поэтому, если вам нужно реализовать свой собственный биндер, вы можете сделать это очень просто и зарегистрировать свой биндер в RequestBinder из любого места вашего приложения.

Поэтому, если вы не хотите использовать отражение для некоторых часто используемых объектов DTO, вы можете реализовать связыватель для типа и зарегистрировать его, и с этого момента он будет использовать ваш собственный связыватель. Во многом это похоже на концепцию MVC ModelBinder.

Отредактировано -

Ниже приведен один файл .cs с кучей классов, которые я использовал в прошлом, чтобы делать именно то, что вам нужно. Первый MsPropertyAssignerProvider — это тот, с которым вы будете работать на своей странице.

Вы перебираете элементы управления и вызываете метод GetPropertyAssigner, передавая ему имя типа элемента управления. Этот метод возвращает экземпляр ObjectPropertyAssigner, у которого есть один метод SetPropertyValue, которому вы можете передать свой экземпляр объекта и экземпляр элемента управления.

  internal class MsPropertyAssignerProvider
  {
    private Hashtable propertyAssigners;

    internal MsPropertyAssignerProvider()
    {
      propertyAssigners = new Hashtable();
      RegisterPropertyAssigner(typeof(TextBox).ToString(), new TextBoxValueExtractor());
      RegisterPropertyAssigner(typeof(DropDownList).ToString(), new DropDownListValueExtractor());
      RegisterPropertyAssigner(typeof(Label).ToString(), new LabelValueExtractor());
      RegisterPropertyAssigner(typeof(CheckBox).ToString(), new CheckBoxValueExtractor());
    }

    internal void RegisterPropertyAssigner(string identifier, IMsObjectPropertyAssigner assigner)
    {
      if (propertyAssigners.ContainsKey(identifier))
        throw new DuplicatePropertyAssignerRegistrationException(identifier);
      propertyAssigners.Add(identifier, assigner);
    } 

    internal IMsObjectPropertyAssigner GetPropertyAssigner(string identifier)
    {
      return (propertyAssigners.ContainsKey(identifier)) ? (IMsObjectPropertyAssigner)propertyAssigners[identifier] : null;
    }
  }

Сопровождающий класс указан ниже

  public interface IMsObjectPropertyAssigner
  {
    void SetPropertyValue(object obj, System.Web.UI.Control control); 
  }

  internal abstract class BaseValueExtractor : IMsObjectPropertyAssigner
  {
    protected MsReflectionHelper reflectionHelper = new MsReflectionHelper();
    protected string FixStringForNumber(string stringValue)
    {
      if (stringValue.Length == 0)
        return "0";
      else
        return stringValue;
    }
    public abstract void SetPropertyValue(object obj, System.Web.UI.Control control);
  }

  internal class TextBoxValueExtractor : BaseValueExtractor
  {
    public override void SetPropertyValue(object obj, System.Web.UI.Control control)
    {
      TextBox textBox = (TextBox)control;
      PropertyInfo propInfo = reflectionHelper.GetPropertyInfo(obj, control.ID);
      Type propType = propInfo.PropertyType;
      if (propType == typeof(System.String))
        reflectionHelper.SetPropertyValue(obj, control.ID, textBox.Text);
      else if (propType == typeof(System.Int16))
        reflectionHelper.SetPropertyValue(obj, control.ID, Int16.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
      else if (propType == typeof(System.Int32))
        reflectionHelper.SetPropertyValue(obj, control.ID, Int32.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
      else if (propType == typeof(System.Int64))
        reflectionHelper.SetPropertyValue(obj, control.ID, Int64.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
      else if (propType == typeof(System.Double))
        reflectionHelper.SetPropertyValue(obj, control.ID, Double.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
      else if (propType == typeof(System.Single))
        reflectionHelper.SetPropertyValue(obj, control.ID, Single.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
      else
        reflectionHelper.SetPropertyValue(obj, control.ID, textBox.Text);
    }
  }

  internal class DropDownListValueExtractor : BaseValueExtractor
  {
    public override void SetPropertyValue(object obj, System.Web.UI.Control control)
    {
      DropDownList dropDownList = (DropDownList)control;
      reflectionHelper.SetPropertyValue(obj, control.ID, dropDownList.SelectedValue);
    }
  }

  internal class LabelValueExtractor : BaseValueExtractor
  {
    public override void SetPropertyValue(object obj, Control control)
    {
      Label label = (Label)control;
      reflectionHelper.SetPropertyValue(obj, control.ID, label.Text);
    }
  }

  internal class CheckBoxValueExtractor : BaseValueExtractor
  {
    public override void SetPropertyValue(object obj, Control control)
    {
      CheckBox checkbox = (CheckBox)control;
      reflectionHelper.SetPropertyValue(obj, control.ID, checkbox.Checked);
    }
  }

Извините, что бы я ни делал, редактор полностью портит листинг кода. Но я надеюсь, что это поможет.

person Shiv Kumar    schedule 08.11.2010
comment
Только 1-й и последний абзацы имеют какое-то отношение к поднятому вопросу. Я не против использования отражения. Так работала проприетарная библиотека. - person Sherlock; 09.11.2010
comment
Я предполагаю, что в вашем случае, поскольку вы работаете с Controls, решение немного сложнее. Класс DtoBinder может создать экземпляр вашего объекта Dto и присвоить все значения свойств с учетом коллекции NameValues. Итак, что вам нужно сделать, это собрать эту коллекцию из имен и значений элемента управления и передать ее DtoBinder. - person Shiv Kumar; 09.11.2010
comment
Только что отредактировал свой первоначальный ответ с некоторыми дополнительными классами, которые я использовал в прошлом. - person Shiv Kumar; 09.11.2010

Разве вы не могли бы использовать AutoMapper для чего-то подобного? Просто настройте свои карты, и он создаст новые объекты и скопирует в них значения.

person Erik Funkenbusch    schedule 06.09.2011

Это довольно старый вопрос, но я столкнулся с ним, пытаясь понять, как на самом деле работает связыватель модели по умолчанию.

У меня есть проект на CodeProject, который на самом деле делает то, что вы хотите (редактор), посмотрите.

Ваше здоровье!

person AlexCode    schedule 29.05.2013