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-запросов нам понадобится трассировщик. Вот пример того, который использовал я:

  1. public class ZhukBlogTracer : TextWriter
  2. {
  3.   public override Encoding Encoding
  4.   {
  5.     get { return Encoding.UTF8; }
  6.   }
  7.  
  8.   public override void WriteLine(string value)
  9.   {
  10.     Debug.WriteLine(string.Empty);
  11.     Debug.WriteLine("---------------------------------------------------------");
  12.     Debug.WriteLine(value);
  13.     Debug.WriteLine("---------------------------------------------------------");
  14.  
  15.   }
  16.  
  17.   public override void Flush()
  18.   {
  19.     Debug.Flush();
  20.   }
  21. }

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

Count()

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

  1. var branchQnt = ctx.Branches
  2.   .Where(b => b.City != "Moscow")
  3.   .Count(); // LINQ to Objects !
  4. var branchQntQuick = ctx.Branches
  5.   .Where(b => b.City != "Moscow")
  6.   .Select(b => b.Id) // ViewFields
  7.   .Count();

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

  1. <View>
  2.  <Query>
  3.   <Where>
  4.    <And>
  5.     <BeginsWith>
  6.      <FieldRef Name="ContentTypeId" />
  7.      <Value Type="ContentTypeId">0x01007087DA17F20149E3A4602E517E6E8EEF</Value>
  8.     </BeginsWith>
  9.     <Neq>
  10.      <FieldRef Name="City" />
  11.      <Value Type="Text">Moscow</Value>
  12.     </Neq>
  13.    </And>
  14.   </Where>
  15.  </Query>
  16.  <ViewFields>
  17.   <FieldRef Name="ID" />
  18.  </ViewFields>
  19.  <RowLimit Paged="TRUE">2147483647</RowLimit>
  20. </View>

Take(n), Skip(n)

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

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

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

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

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

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

JOIN'ы

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

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

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

  1. <View>
  2.  <Query>
  3.   <Where>
  4.    <And>
  5.     <And>
  6.      <BeginsWith>
  7.       <FieldRef Name="ContentTypeId" />
  8.       <Value Type="ContentTypeId">0x010078B0DD38574940478CF9E129FCD65E9B</Value>
  9.      </BeginsWith>
  10.      <Contains>
  11.       <FieldRef Name="DepartmentTitle" />
  12.       <Value Type="Lookup">IT</Value>
  13.      </Contains>
  14.     </And>
  15.     <Contains>
  16.      <FieldRef Name="Title" />
  17.      <Value Type="Text">Zhukov</Value>
  18.     </Contains>
  19.    </And>
  20.   </Where>
  21.  </Query>
  22.  <ViewFields>
  23.   <FieldRef Name="CellPhone" />
  24.   <FieldRef Name="AccessLevel" />
  25.   <FieldRef Name="Manager" />
  26.   <FieldRef Name="Department" />
  27.   <FieldRef Name="ID" />
  28.   <FieldRef Name="owshiddenversion" />
  29.   <FieldRef Name="FileDirRef" />
  30.   <FieldRef Name="Title" />
  31.   <FieldRef Name="Author" />
  32.   <FieldRef Name="Editor" />
  33.  </ViewFields>
  34.  <ProjectedFields>
  35.   <Field Name="DepartmentTitle" Type="Lookup" List="Department" ShowField="Title" />
  36.  </ProjectedFields>
  37.  <Joins>
  38.   <Join Type="LEFT" ListAlias="Department">
  39.    <!--List Name: Departments-->
  40.    <Eq>
  41.     <FieldRef Name="Department" RefType="ID" />
  42.     <FieldRef List="Department" Name="ID" />
  43.    </Eq>
  44.   </Join>
  45.  </Joins>
  46.  <RowLimit Paged="TRUE">2147483647</RowLimit>
  47. </View>

ObjectTrackingEnabled

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

Исходные коды проекта можно скачать здесь.


Поделиться

Коментарии