Linq to Sharepoint. Особенности
Часть 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, знают, что запрос вида:
using (var ctx = new HRDataContext(webUrl))
{
var emps = ctx.EmployeeCollection.First(e => e.Id == id);
}
равнозначен запросу:
using (var ctx = new HRDataContext(webUrl))
{
var emps = ctx.EmployeeCollection.Where(e => e.Id == id).First();
}
Вот только это не работает в SharePoint'е. Там этот запрос равнозначен следующему:
using (var ctx = new HRDataContext(webUrl))
{
var emps = ctx.EmployeeCollection.ToList().Where(e => e.Id == id).First();
}
Если тестировать приложение на 10-20 записях в списке, то можно не заметить падения производительности. Такая бага может проявить себя далеко после сдачи решения заказчику.
Вывод
не использовать .First(
2. Аналог TSQL-оператора IN
В Linq-To-SharePoint его нет, поэтому выражения вида:
using (var ctx = new HRDataContext(_webUrl))
{
var vipIds = new [] { 1, 5, 9, 23, 57, 198, 345);
var emps = ctx.EmployeeCollection.Where(e => vipIds.Contains(e.Id));
}
работать не будут. Вместо этого произойдет исключение. А чтобы его не было надо явно выгружать данные из списка в память (например .ToList()) и только потом использовать .Contains().
Вывод
использовать .Contains() только над коллекциями в памяти
3. Выбор данных из папки
По умолчанию Linq-to-SharePoint выбирает элементы списка/библиотеки только из корневого каталога. Чтобы заставить его вытащить данные из дочерних папок или из какой-то конкретной папки надо использовать метод .ScopeToFolder(rootFolder,recursiveFlag), где
- **rootFolder **- папка, из которой выбираем данные (для корневой папки можно передать string.Empty);
- recursiveFlag- флаг, определяющий просмотр дочерних папок (флаг поднят - смотрим, опущен - не обращаем внимание на дочерние папки)
Используем примерно так:
using (var ctx = new HRDataContext(_webUrl))
{
string.Empty
var emps = ctx.EmployeeCollection.ScopeToFolder(string.Empty);
}
Я рекомендовал бы при разработке сразу учитывать эту особенность и не ждать, пока пользователи начнут строить структуру папок внутри списка и удивляться, что система работает странновато.
Вывод
планировать возможность создания папок в списках/библиотеках и учитывать
это при разработке.
4. Сохранение элемента в папке списка/библиотеки
Данные из папки выбирать научились, теперь надо бы уметь и сохранять/создавать элемент в папке. Для этого просто указываем путь к папке в свойстве Path элемента. Примерно вот так:
var query = ctx.DocumentCardCollection
.ScopeToFolder(string.Empty, true)
.Where(d => d.Date >= DateTime.Today.AddDays(-5));
var card = query.First();
card.Path = "/WebUrl/ListUrl/Folder1/SubFolder2";
Вывод
см. вывод в п.3
Пользуемся на здоровье и покоряем сердца заказчиков