четверг, 11 марта 2010 г.

Пишем CMS. Шаг № 6. Добавление нового языка

Продолжим.
Сегодня мы добавим функциональность на страницу LanguageEdit.aspx, и сможем добавлять новые языки или редактировать уже существующие.

Для начала определим две переменные: одна будет представлять собой объект Language, который мы создали еще раньше с помощью ADO.NET Framework Entity, а вторая - собственно инструмент работы с БД - контекст:
private Language languageObject;
private DatabaseConnection context;


* This source code was highlighted with Source Code Highlighter.


Далее в обработчик загрузки страницы Page_Load добавим проверку на наличие id:
      if (Request["id"] != null)
      {
        int id = 0;
        int.TryParse(Request["id"], out id);
        if (id != 0)
        {
          try
          {
            context = new DatabaseConnection();
              var lan = from l in context.Language
                   where l.LanguageId == id
                   select l;
              languageObject = (Language)lan.First();
          }
          catch
          {
            //log
          }
        }
      }
      if (languageObject == null)
      {
        languageObject = new Language();
      }
      if (!IsPostBack)
        Page.DataBind();


* This source code was highlighted with Source Code Highlighter.


Логика работы этого блока проста: если мы получаем в качестве параметра id, то извлекаем из базы объект по этому id. Если же параметра нет, значит, мы хотим создать новый язык.

В обработчик события Unload можно добавить "уничтожение" объекта context.Dispose(), хотя, полагаю, ресурсы и так будут автоматически очищены по завершении обработки страницы.

Теперь разместим на странице наши контролы, для начала это буду два поля ввода - в одном заголовок языка, во втором его кодовое представление (например, "en-EN"), с его помощью удобно сразу выставлять региональные настройки. Позднее мы добавим контрол загрузки файла, с помощью которого будем грузить на сервер иконки с изображением флага, а пока надо разобраться с основными вещами.

Мы подошли к ключевому вопросу - привязка данных к форме. ASP.NET предлагает несколько контролов для привязки данных, но после некоторой возни с ним мне пришлось отказаться от их использования. Фактически, только два контрола - DetailsView и FormView - позволяют отображать данные по одному объекту, но мне так и не удалось заставить их привязываться к EntityObject. По какой-то, неведомой мне причине, эти контролы в качестве источника данных требуют обязательно объект, который наследут IListSource, IEnumerable, или IDataSource. Наш объект EntityObject не реализует ни один из этих интерфейсов. В качестве источника данных можно было бы использовать ObjectDataSource, но для этого нужно соблюсти определенные требования к классам доступа DAL - поскольку мы используем классы, сгенерированные EntityFramework, использование их не получается. Можно, конечно, было бы написать обертку к этим классам, но плодить лишний код не хочется совершенно.

После некоторых раздумий, а также просмотра обновленного кода проекта TheBeerHouse пришлось остановится на простой односторонней привязке данных к объекту. Что это значит? Очень просто - при открытии страницы мы создаем объект, а наши поля, привязанные к нему, заполняются автоматически. Когда мы хотим сохранить измененный объект, мы извлекаем содержимое полей вручную. Полагаю, ручное извлечение неизбежно - ведь объект еще не сохранен, а значит, поля невозможно привязать. Если кто подскажет более удачное решение, будет здорово.

Итак, создаем на странице простое свойство, которое имеет только метод get и возвращает наш объект:
    protected Language LanguageObject
    {
      get
      {
        return languageObject;
      }
    }


* This source code was highlighted with Source Code Highlighter.


На страницу добавляем текстбоксы и осуществляем привязку, например:
<asp:TextBox ID="txtLangCode" runat="server"
          Text="<%# LanguageObject.LanguageCode %>" Width="214px" />


* This source code was highlighted with Source Code Highlighter.


А теперь вернемся к первому коду, обратите внимание:
if (!IsPostBack)
    Page.DataBind();


* This source code was highlighted with Source Code Highlighter.


Этот код проверяет, был ли постбэк, и, если нет, привязывает все контролы формы. Если мы будем делать привязку и после постбэка, то введенные нами данные в форму будут затерты извлеченными из объекта, что нам совсем не нужно.

Ну вот, осталось дело за малым: сохранить измененные данные. Кидаем на форму кнопку, добавляем событие Click и в обработчике пишем примерно такой код:
bool result = false;
try
      {
        if (context == null)
          context = new PBPWebDatabaseConnection();
        if (languageObject != null)
        {
          languageObject.LanguageTitle = Server.HtmlEncode(txtLangName.Text);
          languageObject.LanguageCode = Server.HtmlEncode(txtLangCode.Text);
          if (filePathToUpload != "")
            languageObject.IconName = Path.GetFileName(filePathToUpload);
          if (languageObject.LanguageId == 0)
            context.AddObject("Language", languageObject);

          int saved = context.SaveChanges();
          if (saved == 1)
          {
            panelInfo.Activate(true, Messages.LanguageSaveOK, Messages.LabelLinkToLanguages, Pages.Languages);
            Master.AddRedirect(Pages.Languages, 4);
            panelMain.Visible = false;
            result = true;
          }
        }
      }
      catch (Exception ex)
      {
        //log
      }
      if (!result)
        panelInfo.Activate(false, Messages.LanguageSaveFail, string.Empty, string.Empty);


* This source code was highlighted with Source Code Highlighter.


Сначала мы проверяем, не нуль ли контекст. Если вдруг он равен null, создаем новый. Объект languageObject уже должен быть создан к этому времени (пустой или взятый из базы). Перезаписываем его свойства данными из полей формы. Затем, если объект был заново созданный (id у него равно 0), то добавляем в контекст. Если нет, ничего не делаем, потому что в этом случае объект уже привязан к контексту (это произошло, когда мы извлекали объект по id). Осталось просто сохранить объект и отреагировать на полученный результат.

Вроде ничего сложного. Если что непонятно, спрашивайте. А загрузку файлов мы чуть попозже обсудим.

Комментариев нет: