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

15 марта 2011 г.

Часть 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

В SharePoint 2010 появилась замечательная вещь под названием Linq-to-SharePoint. Не без особенностей конечно. Вот некоторые из тех, что я "нашел".

1. First() / FirstOrDefault()

Те, кто работал с Linq-to-SQL или Entity Framework, знают, что запрос вида:

  1. using (var ctx = new HRDataContext(webUrl))
  2. {
  3.   var emps = ctx.EmployeeCollection.First(e => e.Id == id);
  4. }
равнозначен запросу:
  1. using (var ctx = new HRDataContext(webUrl))
  2. {
  3.   var emps = ctx.EmployeeCollection.Where(e => e.Id == id).First();
  4. }
Вот только это не работает в SharePoint'е. Там этот запрос равнозначен следующему:
  1. using (var ctx = new HRDataContext(webUrl))
  2. {
  3.   var emps = ctx.EmployeeCollection.ToList().Where(e => e.Id == id).First();
  4. }
Если тестировать приложение на 10-20 записях в списке, то можно не заметить падения производительности. Такая бага может проявить себя далеко после сдачи решения заказчику.
Вывод: не использовать .First(<filter>) без предварительной фильтрации.

2. Аналог TSQL-оператора IN

В Linq-To-SharePoint его нет, поэтому выражения вида:

  1. using (var ctx = new HRDataContext(_webUrl))
  2. {
  3.   var vipIds = new [] { 1, 5, 9, 23, 57, 198, 345);
  4.   var emps = ctx.EmployeeCollection.Where(e => vipIds.Contains(e.Id));
  5. }
работать не будут. Вместо этого произойдет исключение. А чтобы его не было надо явно выгружать данные из списка в память (например .ToList()) и только потом использовать .Contains().
Вывод: использовать .Contains() только над коллекциями в памяти

3. Выбор данных из папки

По умолчанию Linq-to-SharePoint выбирает элементы списка/библиотеки только из корневого каталога. Чтобы заставить его вытащить данные из дочерних папок или из какой-то конкретной папки надо использовать метод .ScopeToFolder(rootFolder,recursiveFlag), где
rootFolder - папка, из которой выбираем данные (для корневой папки можно передать string.Empty);
recursiveFlag - флаг, определяющий просмотр дочерних папок (флаг поднят - смотрим, опущен - не обращаем внимание на дочерние папки)
Используем примерно так:

  1. using (var ctx = new HRDataContext(_webUrl))
  2. {
  3.   string.Empty
  4.   var emps = ctx.EmployeeCollection.ScopeToFolder(string.Empty);
  5. }
Я рекомендовал бы при разработке сразу учитывать эту особенность и не ждать, пока пользователи начнут строить структуру папок внутри списка и удивляться, что система работает странновато.
Вывод: планировать возможность создания папок в списках/библиотеках и учитывать это при разработке

4. Сохранение элемента в папке списка/библиотеки

Данные из папки выбирать научились, теперь надо бы уметь и сохранять/создавать элемент в папке. Для этого просто указываем путь к папке в свойстве Path элемента. Примерно вот так:

  1. var query = ctx.DocumentCardCollection
  2.         .ScopeToFolder(string.Empty, true)
  3.         .Where(d => d.Date >= DateTime.Today.AddDays(-5));
  4. var card = query.First();
  5. card.Path = "/WebUrl/ListUrl/Folder1/SubFolder2";

Вывод: см. вывод в п.3

Пользуемся на здоровье и покоряем сердца заказчиков

Поделиться

Комментарии