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

Миф 1. Linq to SharePoint работает медленно

Это стандартная мулька про Linq to SharePoint. Логика здесь следующая: Linq to SharePoint - это "обертка" поверх CAML-запросов, следовательно он работает медленнее, чем заранее сформированный CAML-запрос. Вот только здесь есть два нюанса:

Во-первых, заранее сформированный CAML-запрос - это вариант мало применимый в реальных приложениях, он хорош для примеров: заранее заточенные требования, не препятствующие ограничениям и минимум функциональности, с целью "сфокусироваться на сути".

Во-вторых, результат, получаемый нативными средствами SharePoint - это объект SPListItem, который надо еще преобразовать в применимый для бизнес-приложений объект. Элементарная задача, когда надо добавить вычисляемое поле, например, булево поле, которое отвечает за признак того, что объект последний раз изменялся более месяца назад в правильной бизнес-сущности выглядит вот так:

  1. public bool IsOld
  2. {
  3.     get
  4.     {
  5.         //ModifiedDate - свойство типа DateTime
  6.         return (DateTime.Today - ModifiedDate).TotalDays;
  7.     }
  8. }

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

В случае с SPListItem этот элементарный трюк не пройдет. Для придания коду читабельности и правильности придется мапить его на бизнес-объект. И в этом случае Linq to SharePoint - лучшая технология доступа к данным в Sharepoint 2010/2013. В сравнении с Camlex.NET Linq to SharePoint намного быстрее. Подробнее можно почитать мой пост Linq to SharePoint. Сравнение производительности с Camlex.NET.

Миф 2. Linq to SharePoint не поддерживает локализацию

При использовании SPMetal код, который им сгенерирован для объекта, описывающего тип содержимого имеет следующий вид:

  1. [ContentType(
  2.     Id = "0x01"//ID типа содержимого 
  3.     List = "Название списка"
  4.     Name = "Название типа содержимого")]
  5. public class ListItem : ITrackEntityState, ITrackOriginalValues, 
  6.     INotifyPropertyChanged, INotifyPropertyChanging
  7. {
  8.     // bla-bla
  9. }

Конечно в таком представлении нельзя локализовать параметры атрибута: локализации в нем не реализована и унаследоваться от него, усложнив логику, тоже нельзя - он запечатанный (sealed). И тут начинается паника.

А решение данной проблемы крайне тривиально - просто не указывать значения свойств Name и List в атрибуте. Вот такой вариант будет работать ровно также:

  1. [ContentType(Id = "0x01")]
  2. public class ListItem : ITrackEntityState, ITrackOriginalValues, 
  3.     INotifyPropertyChanged, INotifyPropertyChanging
  4. {
  5.     // bla-bla
  6. }

Что касается метода DataContext.GetList<T> и обертки для него, которую генерирует SPMetal, то здесь уже можно вдоволь порезвиться и выдернуть имя списка хоть из ресурсов, хоть по его идентификатору, хоть по URL'у.

Очередной миф разрушен, идем дальше.

Миф 3. Linq to SharePoint всегда фильтрует данные по типу содержимого

Последний на сегодня миф, связанный с тем, что для каждого типа содержимого создается отдельный класс и выборка данных из списка основана на получении коллекции этих объектов. И так как в одном списке SharePoint могут находится данные разных типов, то при выборке присутствует фильтрация по типу содержимого. На форумах частенько на это жалуются и указание в качестве типа контента общего родительского (например, 0x01) не помогает.

Для примера я создал список в SharePoint, содержащем два типа содержимого: Элемент и Контакт. С демонстрационными данными:

Стандартный запрос будет выглядеть примерно так:

  1. <View>
  2.     <Query>
  3.         <Where>
  4.             <BeginsWith>
  5.                 <FieldRef Name="ContentTypeId" />
  6.                 <Value Type="ContentTypeId">0x01</Value>
  7.             </BeginsWith>
  8.         </Where>
  9.     </Query>
  10.     <ViewFields>
  11.         <FieldRef Name="ID" />
  12.         <FieldRef Name="owshiddenversion" />
  13.         <FieldRef Name="FileDirRef" />
  14.         <FieldRef Name="Title" />
  15.         <FieldRef Name="Author" />
  16.         <FieldRef Name="FirstName" />
  17.         <FieldRef Name="FullName" />
  18.         <FieldRef Name="ContentType" />
  19.     </ViewFields>
  20.     <RowLimit Paged="TRUE">2147483647</RowLimit>
  21. </View>

И проблема появляется сама собой: как убрать фильтр по типу содержимого? Очень просто. Чтобы за один запрос к списку получить все данные, содержащиеся в нем, надо просто удалить атрибут ContentType из класса. Тогда запрос сразу превращается примерно в такой:

  1. <View>
  2.     <Query></Query>
  3.     <ViewFields>
  4.         <FieldRef Name="ID" />
  5.         <FieldRef Name="owshiddenversion" />
  6.         <FieldRef Name="FileDirRef" />
  7.         <FieldRef Name="Title" />
  8.         <FieldRef Name="Author" />
  9.         <FieldRef Name="FirstName" />
  10.         <FieldRef Name="FullName" />
  11.         <FieldRef Name="ContentType" />
  12.     </ViewFields>
  13.     <RowLimit Paged="TRUE">2147483647</RowLimit>
  14. </View>

Для типов содержимого, которые не имеют свойств, подлежащих выборке из списка/библиотеки документов, данные свойства будут иметь значения null.

Последний на сегодня миф разрушен. Переходим к жестокой правде.

Правда 1. Linq to SharePoint не умеет обрабатывать членство пользователя в группе

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

Правда 2. Linq to SharePoint не работает со всеми типами полей

Такие типы полей как: метаданные, рейтинг и прочие, появившиеся относительно недавно (SharePoint 2010/2013) не подвластны Linq to SharePoint. В этом случае его возможности ограничиваются на проверку равенства/неравенства null. В более сложных случаях придется загружать данные в память и использовать Linq to Objects.

Правда 3. Linq to SharePoint не умеет разбивать результаты на страницы

К чести Linq to SharePoint стоит обратить внимание та то, что и нативный механизм разбиения результатов на страницы далек от совершенства. За примерами опять же идем на блог Игоря Акимова и читаем пост Разбиение запроса SPQuery на страницы. Решение здесь простое - строить такие запросы, чтобы пользователь не уходил дальше 2-3 страницы.

Зачем нужен Linq to SharePoint?

Linq to SharePoint незаменим при построении решений, которые используются данные в списках/библиотеках вместо внешних баз данных. Ровно как и другие Linq-провайдеры. Если данные хранятся в SQL используйте EntityFramework или Linq to SQL, если данные в списках/библиотеках SharePoint, то используйте Linq to SharePoint. Но если ваше решение основано на таксономии или поиске, то Linq to SharePoint - не ваш подход. В остальных случаях я советую использовать именно его.


Поделиться

Коментарии