пятница, 24 июня 2011 г.

Метаданные для SPField

У объекта SPField есть два полезных метода: GetCustomProperty и SetCustomProperty. Но к сожалению, использовать их в качестве обычного PropertyBag очень сложно:
  1. Свойства эти можно использовать только для Custom Field Type'ов, прописывая их названия и типы в схеме типа поля
  2. Вдобавок, нужно еще изменять схему экземпляра поля (SPField.Schema), добавляя туда атрибут Customization. Автоматически это почему-то не делается :(
Естественно, динамически добавлять свойства к уже существующим полям с помощью этих методов никак не получится. Однако, решения есть, и они известны. Дело в том, что схема поля (SPField.Schema) позволяет использовать произвольные атрибуты для элемента Field.

Обычно, для того, чтобы их задействовать, используют private методы класса SPField: SetFieldAttributeValue и GetFieldAttributeValue. Но в этом случае, естественно, используется Reflection, который запрещен в Office365. И вообще, Reflection - это хак, а любые хаки желательно обходить стороной.

Поэтому, я написал собственное решение, которое парзит XML и меняет свойство FieldSchema самостоятельно. Решение получилось довольно простым, и я всё чаще и чаще использую этот самодельный PropertyBag для хранения разных дополнительных атрибутов полей.

Код (не забудьте подключить пространство имен System.Xml.Linq):

  private const string nameSpace = "my";

  /// <summary>
  /// Прикрепляем метаданные к колонке списка. Эти метаданные будут храниться
  /// в схеме поля.
  /// </summary>
  /// <param name="field">Колонка (SPField), к которой следует добавить метаданные</param>
  /// <param name="key">Название поля метаданных</param>
  /// <param name="value">Значение поля метаданных</param>
  public void SetFieldMetaData(SPField field, string key, string value)
  {
   var fieldSchema = XDocument.Parse(field.SchemaXml);
   var tabAttribute = fieldSchema.Element("Field").Attribute(XNamespace.Get(nameSpace) + key);
   if (tabAttribute == null)
    fieldSchema.Element("Field").Add(new XAttribute(XNamespace.Get(nameSpace) + key, value));
   else
    tabAttribute.Value = value;

   field.SchemaXml = fieldSchema.ToString();
   field.Update();
  }

  /// <summary>
  /// Получаем метаданные из колонки.
  /// </summary>
  /// <param name="field">Колонка, из которой считываются метаданные</param>
  /// <param name="key">Название поля метаданных</param>
  /// <returns>Возвращает значение поля метаданных для колонки, или null, если поле метаданных с указанным именем не было ранее присоединено к этой колонке.</returns>
  public string GetFieldMetaData(SPField field, string key)
  {
   var fieldSchema = XDocument.Parse(field.SchemaXml);
   var tabAttribute = fieldSchema.Element("Field").Attribute(XNamespace.Get(nameSpace) + key);
   if (tabAttribute == null)
    return null;
   else
    return tabAttribute.Value;
  }

8 комментариев:

  1. Андрей, добрый день)
    Я правильно понял. чтобы разнести по вкладкам поля списка в этом посте - http://omlin.blogspot.com/2011/06/list-form-sharepoint.html - использовался как раз этот способ присваивания дополнительных атрибутов полю?

    ОтветитьУдалить
  2. Роман, привет!

    Да, всё именно так. В метаданных запоминалось, к какой вкладке принадлежит поле, потом эти сведения использовались в ListFormIterator, чтобы разнести поля по вкладкам.

    ОтветитьУдалить
  3. Андрей,здравствуйте. Мне очень помогла ваша статья про вкладки на ListForm. Я попробовала определить доп атрибут каждому полю как написано в этой статье. Но у меня все время выдает ошибку "В экземпляре объекта не задана ссылка на объект".
    Функцию установки значения атрибута я вызываю так:
    SPList lista = web.Lists["Тест"];
    for (int i = 0; i < lista.ContentTypes[0].Fields.Count; i++)
    {
    SPField next = lista.ContentTypes[0].Fields[i];
    SetFieldMetaData(next, "part", next.DefaultValue);
    }
    Не могли бы вы подсказать как исправить ошибку. Заранее спасибо.

    ОтветитьУдалить
    Ответы
    1. Ксения, здравствуйте!

      Попробуйте добавить проверку на null, как-нибудь так:

      SetFieldMetaData(next, "part", next.DefaultValue ?? String.Empty);

      Удалить
    2. когда я написала проверку, выдавалась ошибка что поле не связано со списком. Видимо нельзя обращаться к полям через типы контента и изменять их атрибуты. Если через список lista.Fields.. то все работает)

      Удалить
  4. Добрый день, к сожалению если использовать ваш метод в эдиторе для филда, тов методо OnSaveChange валиться магическое "0x80070057"
    при попытке выполнить
    field.SchemaXml = fieldSchema.ToString()

    Есть соображения о причине и возможном решении?

    ОтветитьУдалить
    Ответы
    1. Соображений пока нет. А что за эдитор, Custom Field Type чтоли?

      Удалить

Внимание! Реклама и прочий спам будут беспощадно удаляться.