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

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

Получение списка по URL

В базовом классе Microsoft.SharePoint.Linq.DataContext, который мы используем для создание своего контекста данных, есть метод GetList. В качестве параметра этот метод принимает строковое значение, содержащее название (Title) списка. И тут же возникают две проблемы: что делать с локализацией названий списков и как быть в случае переименование списков пользователями.

Другое дело URL списка: пользователь его изменить не может и от локализации он независим. Сложность здесь заключается в том, что у SharePoint нет методов, которые бы возвращали список по его URL. На помощь нам приходит другой метод, а именно SPWeb.GetFolder(string strUrl). Вызвав этот метод, мы получим экземпляр объекта SPFolder, имеющего свойство ParentListId. Теперь сказанное в виде метода:

/// <summary>
/// Получение списка по URL
/// </summary>
private EntityList<T> GetListByUrl<T>(string listUrl)
{
    string listTitle;
    using(var site = new SPSite(Web))
    {
        using(var web = site.OpenWeb())
        {
            var folder = web.GetFolder(listUrl);
            var list = web.Lists[folder.ParentListId];
            listTitle = list.Title;
        }
    }
    return GetList<T>(listTitle);
}

Метод классный, но имеет ряд ограничений: URL должен быть указан относительно текущего узла, например:

"/Lists/Departments", 
"Lists/Departments", 
"/Lists/Departments/AllItems.aspx",
"/DocLib/Forms/AllItems.aspx"

И работать этот метод будет медленнее стандартного, т.к. создаются объекты SPSite, SPWeb, SPFolder и SPList.

Анонимный доступ

Ещё одна досадная неприятность: Linq-to-SharePoint не работает в анонимном режиме. Связано это с тем, что в случае, когда пользователь анонимен, то свойство SPContext.Current.Web.CurrentUser равно null. Из-за чего мы получаем исключение. Чтобы стало понятно, как это обойти достаточно взглянуть на конструктор класса SPServerDataConnection, используемый для получения данных:

public SPServerDataConnection(string url)
{
    if (SPContext.Current != null)
    {
        this.defaultSite = SPContext.Current.Site;
        this.defaultWeb = (SPContext.Current.Web.Url == url) 
            ? SPContext.Current.Web 
            : this.defaultSite.OpenWeb(new Uri(url).PathAndQuery);
    }
    else
    {
        this.defaultSite = new SPSite(url);
        this.defaultWeb = this.defaultSite.OpenWeb(new Uri(url).PathAndQuery);
    }
    if (!this.defaultWeb.Exists)
    {
        throw new ArgumentException(
            Resources.GetString("CannotFindWeb", new object[] { url }));
    }
    this.defaultWebUrl = this.defaultWeb.ServerRelativeUrl;
    this.openedWebs = new Dictionary<string, SPWeb>();
    this.openedWebs.Add(this.defaultWebUrl, this.defaultWeb);
}

Как видно, в случае, когда контекста нет (SPContext.Current == null), он создается заново на основе текущего URL-адреса. Это первый момент. Второй момент заключается в том, что нам нужен пользователь, чтобы выполнить операцию от его имени. В этом нам поможет метод SPSecurity.RunWithElevantPrivilages(), который исполняет переданный ему делегат от имени учетной записи, указанной в пуле веб-приложения. Вот код с описанием:

public static void RunAsAdmin(SPSecurity.CodeToRunElevated secureCode)
{
    // Анонимный доступ
    var isAnonymous = SPContext.Current.Web.CurrentUser == null;
    // Необходимо ли сбрасывать текущий контекст
    var nullUserFlag = (SPContext.Current != null && isAnonymous);
 
    if (nullUserFlag)
    {
        // Сохраняем текущий контекст
        var backupCtx = HttpContext.Current;
        // Сбрасываем текущий контекст
        HttpContext.Current = null;
        // Исполняем код
        SPSecurity.RunWithElevatedPrivileges(secureCode);
        // Возвращаем контекст к предыдущему значению
        HttpContext.Current = backupCtx;
    }
    else
    {
        // Исполняем как есть
        SPSecurity.RunWithElevatedPrivileges(secureCode);
    }
}

Этот статический метод можно вставить в ваш класс, реализующий контекст.

Cross-Site запросы данных в Linq-to-SharePoint

Суть cross-site запросов заключается в том, что контекст созданный для одного сайта может обращаться к данным из другого сайта, но только в пределах одной сайт-коллекции. Возвращаясь к демонстрационному проекту из 2 части, приведу следующий пример: есть два уза с двумя списками, структура которых идентична. Чтобы дать понять контексту, что теперь данные для списка надо брать из другого узла, надо вызвать метод RegisterList(), передав ему в качестве параметров название нового списка, относительный URL нового узла:

using(var ctx = new ZhukBlogContext("http://spserver"))
{
    var rootEmployees = ctx.Employees.ToList();
    ctx.RegisterList<Employees>("Employees", "/SubSite", "Employees");
    var subEmployees = ctx.Employees.ToList();
}

Вот как-то так.

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

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

Техлид, Архитектор, Разработчик, Microsoft MVP. Более 20 лет опыта в области системной интеграции и разработки программного обеспечения. Специализируюсь на проектировании и внедрении масштабируемых высокопроизводительных программных решений в различных отраслях.

Смотрите также

Развертывание списков и библиотек с помощью 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. Особенности. Часть 2

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

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

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

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

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

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

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

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

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