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<T>. В качестве параметра этот метод принимает строковое значение, содержащее название (Title) списка. И тут же возникают две проблемы: что делать с локализацией названий списков и как быть в случае переименование списков пользователями.

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

  1. /// <summary>
  2. /// Получение списка по URL
  3. /// </summary>
  4. private EntityList<T> GetListByUrl<T>(string listUrl)
  5. {
  6.     string listTitle;
  7.     using(var site = new SPSite(Web))
  8.     {
  9.         using(var web = site.OpenWeb())
  10.         {
  11.             var folder = web.GetFolder(listUrl);
  12.             var list = web.Lists[folder.ParentListId];
  13.             listTitle = list.Title;
  14.         }
  15.     }
  16.     return GetList<T>(listTitle);
  17. }

Метод классный, но имеет ряд ограничений: 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, используемый для получения данных:

  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(
  18.             Resources.GetString("CannotFindWeb"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.Current == null), он создается заново на основе текущего URL-адреса. Это первый момент. Второй момент заключается в том, что нам нужен пользователь, чтобы выполнить операцию от его имени. В этом нам поможет метод SPSecurity.RunWithElevantPrivilages(), который исполняет переданный ему делегат от имени учетной записи, указанной в пуле веб-приложения. Вот код с описанием:

  1. public static void RunAsAdmin(SPSecurity.CodeToRunElevated secureCode)
  2. {
  3.     // Анонимный доступ
  4.     var isAnonymous = SPContext.Current.Web.CurrentUser == null;
  5.     // Необходимо ли сбрасывать текущий контекст
  6.     var nullUserFlag = (SPContext.Current != null && isAnonymous);
  7.  
  8.     if (nullUserFlag)
  9.     {
  10.         // Сохраняем текущий контекст
  11.         var backupCtx = HttpContext.Current;
  12.         // Сбрасываем текущий контекст
  13.         HttpContext.Current = null;
  14.         // Исполняем код
  15.         SPSecurity.RunWithElevatedPrivileges(secureCode);
  16.         // Возвращаем контекст к предыдущему значению
  17.         HttpContext.Current = backupCtx;
  18.     }
  19.     else
  20.     {
  21.         // Исполняем как есть
  22.         SPSecurity.RunWithElevatedPrivileges(secureCode);
  23.     }
  24. }

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

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

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

  1. using(var ctx = new ZhukBlogContext("http://spserver"))
  2. {
  3.     var rootEmployees = ctx.Employees.ToList();
  4.     ctx.RegisterList<Employees>("Employees""/SubSite""Employees");
  5.     var subEmployees = ctx.Employees.ToList();
  6. }

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


Поделиться

Коментарии