Linq to SharePoint. Получение мета-данных списка

Одним из недостатков Linq to SharePoint является то, что метаданные списков (поля, типы содержимого и прочее) нельзя получить. При этом сами данные присутствуют, но только как internal. И в случае, когда необходимо проверить какие-нибудь свойства списка, приходится инициализировать объекты SPWeb и SPList. В этом посте я покажу как получать метаданные, не обращаясь напрямую к этим объектам.

Метаданные в Linq to SharePoint

Получить метаданные в Linq to SharePoint можно через свойство DataList объекта EntityList<TEntity>, который реализует интерфейс IBaseList. Вот диаграмма классов, которые мы будем получать из EntityList:

EntityList<TEntity>

Сам EntityList мы получаем, вызывая метод Microsoft.SharePoint.Linq.DataContext.GetList:

  1. [List(Name = "Companies")]
  2. public EntityList<Company> Companies
  3. {
  4.     get
  5.     {
  6.         return GetList<Company>("Companies");
  7.     }
  8. }

Это и будет нашей отправной точкой.

Свои классы метаданных

Так как метаданные мы будем получать, используя рефлексию, то нам понадобятся свои классы - аналоги internal-классов и перечислений Linq to SharePoint. Вот диаграмма классов-дубликатов:

При желании можно её дополнить.

Получение свойств объекта через рефлексию

Для удобства я написал простой метод-расширитель, который будет возвращать значение свойства объекта. В дальнейшем это повысит читабельность кода:

  1. /// <summary>
  2. /// Получение значения своства
  3. /// </summary>
  4. /// <typeparam name="T">Тип свойства</typeparam>
  5. /// <param name="obj">Объект</param>
  6. /// <param name="propName">Имя свойства</param>
  7. public static T GetPropertyValue<T>(this object obj, string propName)
  8. {
  9.     // Получаем тип
  10.     var type = obj.GetType();
  11.     // Получаем свойство
  12.     var prop = type.GetProperty(propName);
  13.     // Получаем значение
  14.     var val = prop.GetValue(obj, null);
  15.     // Приводим к типу T или, если это невозможно,
  16.     // возвращаем значение по умолчанию для типа T
  17.     return val is T
  18.         ? (T)val
  19.         : default(T);
  20. }

Получение метаданных

Принцип получения метаданных из Linq to SharePoint следующий: мы берем internal-объект и производим маппинг его свойств на свойства нашего класса-аналога, за исключением свойств типы данных которых являются ссылочными. В отношении класса SPServerDataList это свойства ContentTypes и Fields. Их придется обрабатывать в "ручную". Все сказанное теперь можно представить в виде метода:

  1. /// <summary>
  2. /// Получение мета данных списка
  3. /// </summary>
  4. /// <param name="entityList">Список</param>
  5. public static EntityListMetaData GetMetaData<T>(EntityList<T> entityList) where T : ZhukDataItem
  6. {
  7.     var res = new EntityListMetaData();
  8.     // Получаем имена свойств для маппинга в дальнейшем
  9.     var propNames = typeof(EntityListMetaData).GetProperties().Select(p => p.Name);
  10.     var entityType = typeof(EntityList<T>);
  11.     // Получаем приватное поле field
  12.     var listField = entityType.GetField("list"
  13.         BindingFlags.NonPublic | BindingFlags.Instance);
  14.     // Получаем значение поля field
  15.     var listValue = listField.GetValue(entityList);
  16.     // Получаем тип этого поля (Microsoft.SharePoint.Linq.Provider.SPServerDataList)
  17.     var listType = listValue.GetType();
  18.     // Получаем свойства типа SPServerDataList
  19.     var listProperties = listType.GetProperties();
  20.     foreach (var listProperty in listProperties)
  21.     {
  22.         // Если поле отсутствует в нашем классе, то пропускаем его
  23.         if (!propNames.Contains(listProperty.Name)) continue;
  24.         // Получаем значение поля
  25.         var listPropertyValue = listProperty.GetValue(listValue, null);
  26.         // Свойство ContentTypes обрабатываем в "ручном" режиме
  27.         if (listProperty.Name == "ContentTypes")
  28.         {
  29.             res.ContentTypes = new List<EntityListContentTypeInfo>();
  30.             var ctypes = listPropertyValue as IEnumerable;
  31.             if (ctypes != null)
  32.             {
  33.                 // Перебираем типы содержимого
  34.                 foreach (var ctype in ctypes)
  35.                 {
  36.                     var ct = new EntityListContentTypeInfo
  37.                     {
  38.                         Id = ctype.GetPropertyValue<string>("Id"),
  39.                         Name = ctype.GetPropertyValue<string>("Name"),
  40.                         Description = ctype.GetPropertyValue<string>("Description"),
  41.                         Hidden = ctype.GetPropertyValue<bool>("Hidden")
  42.                     };
  43.                     res.ContentTypes.Add(ct);
  44.                 }
  45.             }
  46.         }
  47.         // Свойство Fields обрабатываем в "ручном" режиме
  48.         else if (listProperty.Name == "Fields")
  49.         {
  50.             res.Fields = new List<EntityListFieldInfo>();
  51.             var fields = listPropertyValue as IEnumerable;
  52.             if (fields != null)
  53.             {
  54.                 foreach (var field in fields)
  55.                 {
  56.                     var ef = new EntityListFieldInfo
  57.                     {
  58.                         Id = field.GetPropertyValue<Guid>("Id"),
  59.                         Title = field.GetPropertyValue<string>("Title"),
  60.                         InternalName = field.GetPropertyValue<string>("InternalName"),
  61.                         FieldType = field.GetPropertyValue<EntityListFieldType>("FieldType"),
  62.                         //AllowMultipleValues = field.GetPropertyValue<bool>("AllowMultipleValues"),
  63.                         //Choices = field.GetPropertyValue<IEnumerable>("Choices"),
  64.                         //FillInChoice = field.GetPropertyValue<bool>("FillInChoice"),
  65.                         Hidden = field.GetPropertyValue<bool>("Hidden"),
  66.                         IsCalculated = field.GetPropertyValue<bool>("IsCalculated"),
  67.                         ReadOnlyField = field.GetPropertyValue<bool>("ReadOnlyField"),
  68.                         Required = field.GetPropertyValue<bool>("Required"),
  69.                         Description = field.GetPropertyValue<string>("Description"),
  70.                         //LookupDisplayColumn = field.GetPropertyValue<string>("LookupDisplayColumn"),
  71.                         //LookupList = field.GetPropertyValue<string>("LookupList"),
  72.                         //PrimaryFieldId = field.GetPropertyValue<string>("PrimaryFieldId")
  73.                     };
  74.                     res.Fields.Add(ef);
  75.                 }
  76.             }
  77.         }
  78.         else
  79.         {
  80.             // Свойство текущего класса
  81.             var property = typeof(EntityListMetaData).GetProperty(listProperty.Name);
  82.             // Задаем полученное значение
  83.             property.SetValue(res, listPropertyValue, null);
  84.         }
  85.     }
  86.     // возвращаем результат
  87.     return res;
  88. }

При инициализации объекта EntityListFieldInfo некоторые строки кода закоментированны, т.к. получение этих свойств возможно только для соответствующих типов полей. Надо добавить логику, связанную с проверкой значения FieldType.

Результат

В результате мы получаем метаданные списка без явной инициализации объектов SPSite, SPWeb и SPList. К тому же Linq to SharePoint кэширует эти данные при первом вызове метода GetList.

Интернет-сайт на базе SharePoint 2010 для модераторов (wiki-страница)

Применение

Использовать этот функционал, я считаю, лучше всего на уровне репозитария. В одном из ближайших постов я расскажу о своем опыте построения репозитариев для работы с данными списков/библиотек документов SharePoint, используя технологию Linq to SharePoint.

Виталий Жуков

Виталий Жуков

SharePoint архитектор, разработчик, тренер, Microsoft MVP (Office Development). Более 15 лет опыта работы с SharePoint, Dynamics CRM, Office 365, и другими продуктами и сервисами Microsoft.

Смотрите также

Развертывание списков и библиотек с помощью SPFx-решений

Развертывание списков и библиотек с помощью SPFx-решений

SharePoint. Drag-and-Drop Загрузчик файлов

SharePoint. Drag-and-Drop Загрузчик файлов

CSOM. Загрузка файлов

CSOM. Загрузка файлов

SharePoint List REST API. Часть 2

SharePoint List REST API. Часть 2

SharePoint Framework. Создание веб-части на Angular

SharePoint Framework. Создание веб-части на Angular

SharePoint List REST API. Часть 1

SharePoint List REST API. Часть 1

Презентация с доклада о SharePoint Framework

Презентация с доклада о SharePoint Framework

SharePoint Framework. Создаем AngularJS 1.x Client WebPart

SharePoint Framework. Создаем AngularJS 1.x Client WebPart

SharePoint. Регистрация CSS и JavaScript с помощью DelegateControl

SharePoint. Регистрация CSS и JavaScript с помощью DelegateControl

SharePoint. Расширяем REST API

SharePoint. Расширяем REST API

SharePoint Excel Services. Создаем кредитный калькулятор

SharePoint Excel Services. Создаем кредитный калькулятор

SharePoint Ribbon API. Использование ToggleButton

SharePoint Ribbon API. Использование ToggleButton

SharePoint 2013. How To: настройка входящей почты для разработчиков

SharePoint 2013. How To: настройка входящей почты для разработчиков

Мифы и правда о Linq to SharePoint

Мифы и правда о Linq to SharePoint

5 особенностей SPSiteDataQuery

5 особенностей SPSiteDataQuery

SharePoint 2013. Введение в SharePoint App. Часть 2

SharePoint 2013. Введение в SharePoint App. Часть 2

SharePoint 2013. Введение в SharePoint App. Часть 1

SharePoint 2013. Введение в SharePoint App. Часть 1

Превью для веб-части в SharePoint 2010/2013

Превью для веб-части в SharePoint 2010/2013

SharePoint 2013. Еще немного о новых контролах

SharePoint 2013. Еще немного о новых контролах

SharePoint 2013. Контрол ClientPeoplePicker

SharePoint 2013. Контрол ClientPeoplePicker

SharePoint 2013. Контрол ImageCrop

SharePoint 2013. Контрол ImageCrop

SharePoint 2013. Тип поля Geolocation

SharePoint 2013. Тип поля Geolocation

Создание типа поля в SharePoint

Создание типа поля в SharePoint

SharePoint 2010. Длительные операции с обновляемым статусом

SharePoint 2010. Длительные операции с обновляемым статусом

Linq to SharePoint. Создаем ContentIterator

Linq to SharePoint. Создаем ContentIterator

Linq to SharePoint. Получение данных из другой коллекции сайтов

Linq to SharePoint. Получение данных из другой коллекции сайтов

Linq to SharePoint. Версионность

Linq to SharePoint. Версионность

SharePoint. Получение URL-адреса иконки для документа

SharePoint. Получение URL-адреса иконки для документа

SharePoint 2010. PostBack для Fluent Ribbon API

SharePoint 2010. PostBack для Fluent Ribbon API

Linq to SharePoint. Блокировка документов

Linq to SharePoint. Блокировка документов

Linq to SharePoint. Паттерн Repository

Linq to SharePoint. Паттерн Repository

Linq to SharePoint. Мапинг полей

Linq to SharePoint. Мапинг полей

Linq to SharePoint. Формирование данных для ProcessBatchData

Linq to SharePoint. Формирование данных для ProcessBatchData

Linq to SharePoint. Сравнение производительности с Camlex.NET

Linq to SharePoint. Сравнение производительности с Camlex.NET

Linq to SharePoint. Часть 5. Поля Choice и MultiChoice

Linq to SharePoint. Часть 5. Поля Choice и MultiChoice

Linq to SharePoint. Часть 4. Dynamic LINQ

Linq to SharePoint. Часть 4. Dynamic LINQ

Linq to SharePoint. Особенности. Часть 3

Linq to SharePoint. Особенности. Часть 3

Linq to SharePoint. Особенности. Часть 2

Linq to SharePoint. Особенности. Часть 2

SharePoint 2010. PeopleEditor. Установка значения

SharePoint 2010. PeopleEditor. Установка значения

SharePoint 2010. Настройка входящей почты для кастомного списка

SharePoint 2010. Настройка входящей почты для кастомного списка

Linq to Sharepoint. Особенности

Linq to Sharepoint. Особенности

EntityFramework. Оптимистические блокировки

EntityFramework. Оптимистические блокировки