Linq to SharePoint. Получение данных из другой коллекции сайтов
Сегодня я покажу как выполнять Cross-SiteCollection запросы с использованием Linq to SharePoint.
По умолчанию, Linq to SharePoint работает только в пределах одной коллекции узлов (SPSite). Это ограничение связано с объектом SPServerDataConnection, предоставляющего в Linq to SharePoint подключение к спискам/библиотекам документов. Если конкретней, то проблема в конструкторе:
- 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 доступен, то за пределы коллекции сайтов "выйти" нельзя. Трюк, позволяющий обойти это ограничение аналогичен трюку при работе с Linq to SharePoint в анонимном режиме.
Нам необходимо обнулить текущий контекст, чтобы спровоцировать SharePoint создать новый объект SPSite.
Cross-SiteCollection Repository
Инкапсулировать CrossSiteCollection-запросы в репозиторий можно следующим образом: сделать новый конструктор, который принимает булево значение - флаг, отвечающий за инициализацию контекста для другой коллекции сайтов.
- protected BaseRepository(string listName, string webUrl, bool readOnly, bool crossSite)
- {
- ReadOnly = readOnly;
- ListName = listName;
- WebUrl = webUrl;
-
- var ctx = SPContext.Current;
- IsAnonymous = ctx != null && SPContext.Current.Web.CurrentUser == null;
- if (crossSite)
- {
- var backupCtx = HttpContext.Current;
- // Сбрасываем контекст
- HttpContext.Current = null;
- // Инициализируем контекст и прочее
- InitializeParameters();
- HttpContext.Current = backupCtx;
- }
- else
- {
- InitializeParameters();
- }
- }
Применение
В демонстрационном проекте я создал веб-часть CrossSiteCollectionDataQuery, демонстрирующую работу с данными, расположенными в другой коллекции сайтов. Код метода CreateChildControls в нем выглядит вот так (частично):
- // Другая коллекция сайтов
- var repository1 = new EmployeeRepository("http://SharePointServer/linq", true, true);
- var items1 = repository1.GetEntityCollection();
- foreach (var employee in items1)
- {
- // Выводим информацию о сотруднике
- RenderEmployeeInfo(employee);
- }
- Controls.Add(new LiteralControl("***<br/>"));
- // Текущая коллекция сайтов
- var repository2 = new EmployeeRepository("http://SharePointServer/sites/asc", true, false);
- var items2 = repository2.GetEntityCollection();
- foreach (var employee in items2)
- {
- // Выводим информацию о сотруднике
- RenderEmployeeInfo(employee);
- }
И результат работы: