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

Часть 1. First()/FirstOrDefault(), T-SQL IN, Path
Часть 2. Count(), Take(), Skip(), JOIN
Часть 3. Анонимный доступ, Получение списка по URL'у, Cross-Site запросы
Часть 4. SPListItem -> LINQ, Dynamic Linq to SharePoint
Часть 5. Поля Choice и MultiChoice в Linq to SharePoint
Часть 6. Сравнение производительности Linq to SharePoint и Camlex.NET

Для демонстрации я создал простой проект, в котом описаны 4 типа содержимого (Сотрудник, Департамент, Филиал, Компания), поля для них, списки, и классы для этих сущностей:

Диаграмма классов

Диаграмма классов

Трассировка

Во-первых, для проверки построения CAML-запросов нам понадобится трассировщик. Вот пример того, который использовал я:

public class ZhukBlogTracer : TextWriter
{
  public override Encoding Encoding
  {
    get { return Encoding.UTF8; }
  }
 
  public override void WriteLine(string value)
  {
    Debug.WriteLine(string.Empty);
    Debug.WriteLine("---------------------------------------------------------");
    Debug.WriteLine(value);
    Debug.WriteLine("---------------------------------------------------------");
 
  }
 
  public override void Flush()
  {
    Debug.Flush();
  }
}

Он просто выводит построенный CAML в окно Output Visual Studio. Этого для отладки вполне достаточно. Чтобы его задействовать надо просто подставить экземляр этого класса в свойство Log контекста.

Count()

Этот метод-расширитель "не знаком" LinqToSharePoint, т.е. сначала будет неявно получены все элементы, а потом уже произойдет вызов Count(). Но это уже будет Linq to Objects. Если же количество посчитать необходимо, то надо ограничить вывод полей. В случае с количеством будет достаточно выбрать поле ID элемента.

var branchQnt = ctx.Branches
  .Where(b => b.City != "Moscow")
  .Count(); // LINQ to Objects !
var branchQntQuick = ctx.Branches
  .Where(b => b.City != "Moscow")
  .Select(b => b.Id) // ViewFields
  .Count();

В результате будет построен следующий CAML:

<View>
 <Query>
  <Where>
   <And>
    <BeginsWith>
     <FieldRef Name="ContentTypeId" />
     <Value Type="ContentTypeId">0x01007087DA17F20149E3A4602E517E6E8EEF</Value>
    </BeginsWith>
    <Neq>
     <FieldRef Name="City" />
     <Value Type="Text">Moscow</Value>
    </Neq>
   </And>
  </Where>
 </Query>
 <ViewFields>
  <FieldRef Name="ID" />
 </ViewFields>
 <RowLimit Paged="TRUE">2147483647</RowLimit>
</View>

Take(n), Skip(n)

Здесь тоже все не так просто. CAML-запрос имеет параметр RowLimit который ограничивает количество выбираемых элементов, т.е. следующий код укажет RowLimit равным 10:

var branchTake = ctx.Branches
  .Where(b => b.City != "Moscow")
  .Take(10)
  .Count();

Что касается метода Skip, то перед его выполнением будет неявно получены все элементы. Привычный последовательный вызов методов Skip и Take вообще не имеет никакого отношений к Linq to SharePoint:

var branchSkipTake = ctx.Branches
  .Where(b => b.City != "Moscow")
  .Skip(10) // LINQ to Objects !
  .Take(10); // LINQ to Objects !

Если у вас есть функционал с постраничным выводом информации, то максимум, что вы можете сделать, так это поменять местами вызовы этих методов и надеется, что пользователь не полезет дальше 5 страницы:

// pageIndex - индекс текущей страницы
// pageSize - кол-во элементов на странице
var branchPaged = ctx.Branches
  .Where(b => b.City != "Moscow")
  .Take((pageIndex + 1) * pageSize)
  .Skip(pageIndex * pageSize); // LINQ to Objects !

JOIN'ы

JOIN'ы, которые появились в SharePoint 2010 строятся только на основе EntityRef свойств ваших объектов. Задействовать этот функционал вызовом метода-расширителя Join() не получиться. К тому же здесь есть ограничение: ссылаться можно только на одну сущность за один запрос (если надо больше - придется выгружать объекты в память и фильтровать уже там). Вот простой пример

var bigJoin = ctx.Employees
  .Where(x => x.Department.Title.Contains("IT"))
  .Where(x => x.Title.Contains("Zhukov"))
  .ToList();

И долгожданный JOIN в CAML-запросе:

<View>
 <Query>
  <Where>
   <And>
    <And>
     <BeginsWith>
      <FieldRef Name="ContentTypeId" />
      <Value Type="ContentTypeId">0x010078B0DD38574940478CF9E129FCD65E9B</Value>
     </BeginsWith>
     <Contains>
      <FieldRef Name="DepartmentTitle" />
      <Value Type="Lookup">IT</Value>
     </Contains>
    </And>
    <Contains>
     <FieldRef Name="Title" />
     <Value Type="Text">Zhukov</Value>
    </Contains>
   </And>
  </Where>
 </Query>
 <ViewFields>
  <FieldRef Name="CellPhone" />
  <FieldRef Name="AccessLevel" />
  <FieldRef Name="Manager" />
  <FieldRef Name="Department" />
  <FieldRef Name="ID" />
  <FieldRef Name="owshiddenversion" />
  <FieldRef Name="FileDirRef" />
  <FieldRef Name="Title" />
  <FieldRef Name="Author" />
  <FieldRef Name="Editor" />
 </ViewFields>
 <ProjectedFields>
  <Field Name="DepartmentTitle" Type="Lookup" List="Department" ShowField="Title" />
 </ProjectedFields>
 <Joins>
  <Join Type="LEFT" ListAlias="Department">
   <!--List Name: Departments-->
   <Eq>
    <FieldRef Name="Department" RefType="ID" />
    <FieldRef List="Department" Name="ID" />
   </Eq>
  </Join>
 </Joins>
 <RowLimit Paged="TRUE">2147483647</RowLimit>
</View>

ObjectTrackingEnabled

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

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

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

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. Мапинг полей

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

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

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

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

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

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

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

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

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