Linq to SharePoint. Получение данных из другой коллекции сайтов

Сегодня я покажу как выполнять Cross-SiteCollection запросы с использованием Linq to SharePoint.

По умолчанию, Linq to SharePoint работает только в пределах одной коллекции узлов (SPSite). Это ограничение связано с объектом SPServerDataConnection, предоставляющего в Linq to SharePoint подключение к спискам/библиотекам документов. Если конкретней, то проблема в конструкторе:

  1. public SPServerDataConnection(string url)
  2. {
  3.     if (SPContext.Current != null)
  4.     {
  5.         this.defaultSite = SPContext.Current.Site;
  6.         this.defaultWeb = (SPContext.Current.Web.Url == url) 
  7.             ? SPContext.Current.Web 
  8.             : this.defaultSite.OpenWeb(new Uri(url).PathAndQuery);
  9.     }
  10.     else
  11.     {
  12.         this.defaultSite = new SPSite(url);
  13.         this.defaultWeb = this.defaultSite.OpenWeb(new Uri(url).PathAndQuery);
  14.     }
  15.     if (!this.defaultWeb.Exists)
  16.     {
  17.         throw new ArgumentException(Resources.GetString("CannotFindWeb",
  18.                           new object[] { url }));
  19.     }
  20.     this.defaultWebUrl = this.defaultWeb.ServerRelativeUrl;
  21.     this.openedWebs = new Dictionary<string, SPWeb>();
  22.     this.openedWebs.Add(this.defaultWebUrl, this.defaultWeb);
  23. }

Т.е. если SPContext доступен, то за пределы коллекции сайтов "выйти" нельзя. Трюк, позволяющий обойти это ограничение аналогичен трюку при работе с Linq to SharePoint в анонимном режиме.

Нам необходимо обнулить текущий контекст, чтобы спровоцировать SharePoint создать новый объект SPSite.

Cross-SiteCollection Repository

Инкапсулировать CrossSiteCollection-запросы в репозиторий можно следующим образом: сделать новый конструктор, который принимает булево значение - флаг, отвечающий за инициализацию контекста для другой коллекции сайтов.

  1. protected BaseRepository(string listName, string webUrl, bool readOnly, bool crossSite)
  2. {
  3.     ReadOnly = readOnly;
  4.     ListName = listName;
  5.     WebUrl = webUrl;
  6.  
  7.     var ctx = SPContext.Current;
  8.     IsAnonymous = ctx != null && SPContext.Current.Web.CurrentUser == null;
  9.     if (crossSite)
  10.     {
  11.         var backupCtx = HttpContext.Current;
  12.         // Сбрасываем контекст
  13.         HttpContext.Current = null;
  14.         // Инициализируем контекст и прочее
  15.         InitializeParameters();
  16.         HttpContext.Current = backupCtx;
  17.     }
  18.     else
  19.     {
  20.         InitializeParameters();
  21.     }
  22. }

Применение

В демонстрационном проекте я создал веб-часть CrossSiteCollectionDataQuery, демонстрирующую работу с данными, расположенными в другой коллекции сайтов. Код метода CreateChildControls в нем выглядит вот так (частично):

  1. // Другая коллекция сайтов
  2. var repository1 = new EmployeeRepository("http://SharePointServer/linq"truetrue);
  3. var items1 = repository1.GetEntityCollection();
  4. foreach (var employee in items1)
  5. {
  6.     // Выводим информацию о сотруднике
  7.     RenderEmployeeInfo(employee);
  8. }
  9. Controls.Add(new LiteralControl("***<br/>"));
  10. // Текущая коллекция сайтов
  11. var repository2 = new EmployeeRepository("http://SharePointServer/sites/asc"truefalse);
  12. var items2 = repository2.GetEntityCollection();
  13. foreach (var employee in items2)
  14. {
  15.     // Выводим информацию о сотруднике
  16.     RenderEmployeeInfo(employee);
  17. }

И результат работы:


Поделиться

Коментарии