Практическое руководство. Выполнение запроса с помощью LINQ to SharePoint

Дата последнего изменения: 8 октября 2010 г.

Применимо к: SharePoint Foundation 2010

В этой статье
Шаг 1: получение ссылки на веб-сайт
Шаг 2: получение ссылки на список
Шаг 3 (дополнительный): выключение отслеживания изменений объектов
Шаг 4: определение запроса LINQ
Шаг 5: перечисление результатов запроса
Шаг 6 (дополнительный): слияние результатов из нескольких списков и нескольких источников данных
Шаг 7 (дополнительный): объединение результатов из нескольких источников данных

В данном разделе поясняется, как выполнить запрос списков Microsoft SharePoint Foundation с помощью поставщика LINQ to SharePoint.

Шаг 1: получение ссылки на веб-сайт

Создание всего кода для поставщика LINQ to SharePoint должно начинаться с создания объекта DataContext. Этот объект представляет подмножество базы данных контента, а именно списки и элементы списков веб-сайта SharePoint Foundation. Самый простой способ создания такого объекта заключается в передаче абсолютного URL-адреса веб-сайта, для которого необходимо отправить запрос, в виде строкового литерала в конструктор классов, как показано здесь.

DataContext teamSite = new DataContext("http://MarketingServer/SalesTeam");

Однако чаще решение должно работать со многими сайтами на нескольких фермах, поэтому при создании кода полный URL-адрес неизвестен. Если код выполняется в HTTP-контексте, например, в веб-части или на настраиваемой странице приложения, и запрос выполняется для текущего веб-сайта, то для получения URL-адреса можно использовать объект SPContext, как показано в данном примере.

DataContext teamSite = new DataContext(SPContext.Current.Web.Url);

Кроме того, контекст можно использовать для того, чтобы косвенно получить URL-адреса других веб-сайтов из семейства веб-сайтов или даже других семейств веб-сайтов веб-приложения. Например, в следующем коде создается DataContext для веб-сайта верхнего уровня самого старого семейства веб-сайтов в веб-приложении.

String rootOfOldestURL = this.Site.WebApplication.Sites[0].RootWeb.Url;      
using (DataContext topSiteOfOldestSiteCollection = new DataContext(rootOfOldestURL))
{
}

Обратите внимание на то, что объект DataContext необходимо удалить, поскольку он использует объект SPWeb, отличный от предоставленного объектом SPContext.

ПримечаниеПримечание

Ссылки на объект SPWebApplication требуют использования оператора using (Imports в Visual Basic) для пространства имен Microsoft.SharePoint.Administration.

Сведения о других способах получения ссылок на веб-сайты из фермы SharePoint Foundation см. в статье Получение ссылок на сайты, веб-приложения и другие ключевые объекты.

Если HTTP-контекст отсутствует, как например в консольном приложении, и имя сервера на момент создания кода неизвестно, для получения ссылки на корневой веб-сайт можно использовать псевдоним "localhost", как показано в следующих примерах.

using (DataContext topLevelSite = new DataContext("https://localhost"))
{
}

using (DataContext mySite = new DataContext("https://localhost/sites/MySite"))
{
}

Поскольку HTTP-контекст отсутствует, объект DataContext необходимо удалить.

Можно создать производный от DataContext класс, в этом случае необходимо воспользоваться конструктором классов, как показано в следующем примере.

ContosoTeamData teamSite = new ContosoTeamData(SPContext.Current.Web.Url);

Шаг 2: получение ссылки на список

Используйте метод GetList<T>(String) для получения объекта EntityList<TEntity>. Этот объект является представлением IQueryable<T> списка. Ниже приведен соответствующий пример.

EntityList<Announcement> announcements = teamSite.GetList<Announcement>("Announcements")

Обратите внимание на то, что тип контента для списка должен быть представлен явно объявленным классом, в этом случае — классом "Announcement". Этот класс должен быть дополнен атрибутом ContentTypeAttribute, который задает имя типа контента на веб-сайте SharePoint Foundation и идентификатор типа контента. Как правило, при создании кода следует знать, для каких списков в нем отправляются запросы. Класс, который представляет тип контента, должен, как минимум, включать хотя бы одно свойство, представляющее столбец в списке, а объявление этого свойства должно быть дополнено атрибутом ColumnAttribute, который задает хотя бы имя и тип данных поля. В следующем примере показаны минимальные требования к объявлению, которые необходимы для того, чтобы разрешить вызывающему коду вызвать метод GetList<T>(String) (где "T" — это Announcement).

[ContentType(Name="Announcement", Id="0x0104")]
public partial class Announcement
{
    [Column(Name = "Title", FieldType = "Text")] 
    public String Title { get; set; }
}

Однако запрос может обращаться только к столбцам, представленным свойствами в классе типа контента, поэтому, если предоставлено только минимальное объявление, вызывающий код может обращаться только к полю (столбцу) Title, как показано в данном примере.

var excitingAnnouncements = from announcement in announcements
                            where announcement.Title.EndsWith("!")
                            select announcement;
ПримечаниеПримечание

Как показано в данном примере, тот факт, что класс называется Announcement, не означает, что он должен точно соответствовать типу контента SharePoint Foundation с таким же именем. Это значит, что иметь свойство для каждого столбца типа контента необязательно. Класс может представлять только подмножество столбцов типа контента и может иметь дополнительные члены. Именно это и является критически важной характеристикой поставщика LINQ to SharePoint, поскольку столбцы могут добавляться в списки владельцами сайтов, что приводит к созданию нового типа контента для списка. Этот класс также может иметь имя, отличное от имени типа контента списка. Он может иметь любое имя, используемое в качестве параметра типа в вызове метода GetList<T>(String). Но в большинстве случаев код более удобен для чтения, если имя этого класса совпадает с именем официального типа контента списка. По умолчанию в средстве SPMetal используется именно такой подход.

Типы контента могут наследовать от других типов контента, а для вызова метода GetList<T>(String) в качестве параметра типа можно использовать любой тип контента, который располагается выше в дереве наследования. Например, поскольку все типы контента являются производными от базового типа контента Item, Item можно использовать в качестве параметра типа, как показано в данном примере:

EntityList<Item> announcements = teamSite.GetList<Item>("Announcements")

Исходный код должен предоставлять объявление класса Item, а этот класс должен объявлять свойства для каждого из столбцов в типе контента, на который ссылаются запросы.

При использовании типа контента "Item" во время создания кода запросы можно направлять в списки, не зная их имен или производных типов контента, как показано в данном примере.

DataContext topLevelSite = new DataContext("https://localhost");
SPSite siteCollection = new SPSite("https://localhost");
EntityList<Item> someList = topLevelSite.GetList<Item>(siteCollection.RootWeb.Lists[0].Title);

var first3Items = from item in someList
                  where item.Id <= 3
                  select item;

foreach (var item in first3Items)
{
    Console.Writeline("{0} is one of the first 3 items in {1}", item.Title, someList.Title);
}

Однако в наиболее реалистичных сценариях создания полезного кода для LINQ to SharePoint используются столбцы, которые уникальны для определенных списков, поэтому в практических целях необходимо знать имена списков и их производные типы контента. Например, при создании кода необходимо иметь возможность строить предположения о наличии определенных списков на веб-сайтах, для которых будет выполняться код запроса. Кроме того, поскольку запросы для указанного списка относятся к конкретным столбцам, необходимо иметь возможность предположить наличие в списке определенного подмножества столбцов. Этим подразумевается, что решение SharePoint Foundation будет относиться к одному из двух типов:

  • Оно будет предназначено для отправки запросов для хорошо известных типов списков, которые входят в SharePoint Foundation или в продукты с расширенными функциональными возможностями, например Microsoft SharePoint Server.

  • Оно будет предназначено для установки и использования совместно с набором из одного или нескольких настраиваемых списков, предоставляемым в составе набора компонентов или настраиваемого определения сайта.

Для создания требуемых объявлений классов и свойств рекомендуется использовать средство SPMetal.

Шаг 3 (дополнительный): выключение отслеживания изменений объектов

Если код только отправляет вызовы в списки и не добавляет, удаляет или изменяет элементы списков, отслеживание изменений объектов можно выключить. Это позволит увеличить производительность. Установите для свойства ObjectTrackingEnabled значение false.

teamSite.ObjectTrackingEnabled = false;

Шаг 4: определение запроса LINQ

Ценность LINQ заключается в том, что запросы записываются приблизительно одним и тем же способом, независимо от источника данных или поставщика LINQ. За исключением небольших различий в получении ссылки на контекст данных и объект IQueryable<T>, запросы LINQ to SharePoint не отличаются от запросов, используемых для LINQ to SQL или LINQ to XML. Дополнительные сведения см. в статьях, посвященных .NET LINQ для реляционных данных (LINQ to SQL) (Возможно, на английском языке) и LINQ to XML (Возможно, на английском языке).

Однако не бывает двух полностью одинаковых поставщиков LINQ. Различия в собственных языках запросов источников данных (на которые запросы LINQ преобразуются поставщиком) иногда приводят к появлению различных ограничений использования запросов. В частности, существует ограничение на явное или неявное объединение списков в запросах, использующих поставщика LINQ to SharePoint. Запрос LINQ to SharePoint может явно или неявно объединить два списка, но только при условии, что один из них имеет столбец типа "Lookup", который выполняет подстановку в столбец в другой таблице. Если поле "Lookup" допускает использование только одного значения, это отношение между списками должно быть представлено в коде с помощью поля EntityRef<TEntity> в классе, который представляет тип контента списка. Если поле допускает использование нескольких значений, такое отношение должно быть представлено с помощью поля EntitySet<TEntity> и свойства EntitySet<TEntity>, которое заключает его в оболочку.

СоветСовет

Свойство Log представляет собой объект TextWriter, который позволяет записать запрос CAML, в который преобразуется запрос LINQ. Возможность просмотра запроса CAML может быть полезна при отладке. Для этого назначьте объект TextWriter свойству Log. В следующем примере OutTextWriter назначается свойству Log. Это вызывает отображение запроса CAML на консоли при выполнении запроса LINQ в консольном приложении, как показано здесь.

#if DEBUG
teamSite.Log = Console.Out;
#endif

Шаг 5: перечисление результатов запроса

Как и в случае со всеми поставщиками LINQ, запрос LINQ to SharePoint не выполняется до тех пор, пока не выполнено его перечисление. Обычно это происходит в цикле foreach. В нестандартной ситуации, когда требуется настраиваемое перечисление, например пропуск каждого второго элемента в результате, можно использовать методы IEnumerable и IEnumerator.

Кроме того, можно назначить результаты запроса переменной IEnumerable<T>, например объекту IList<T> или ICollection<T>, вместо анонимного типа var (Dim в Visual Basic). При этом запрос выполняется немедленно, чтобы переменную можно было заполнить. Ниже приведен соответствующий пример:

EntityList<Announcement> announcements = teamSite.GetList<Announcement>("Announcements")

IList<Announcement> excitingAnnouncements = from announcement in announcements
                                            where announcement.Title.EndsWith("!")
                                            select announcement;

Шаг 6 (дополнительный): слияние результатов из нескольких списков и нескольких источников данных

Можно выполнять слияние результатов из нескольких списков в один объект IList<T> для его дальнейшей фильтрации с помощью LINQ to Objects. В следующем примере показано, как создать объект IList<T> для корпоративных и групповых событий и как создать отчет по событиям текущего дня, имевшим место в аудитории.

DataContext corpSiteData = new DataContext("https://localhost/CorpSite");
DataContext markTeamData = new DataContext("https://localhost/Marketing");

EntityList<Event> allCorpEvents = corpSiteData.GetList<Event>("Calendar");
EntityList<Event> allMarkTeamEvents = markTeamData.GetList<Event>("Calendar");

List<Event> todaysCorpEvents = (from ev in allCorpEvents
                  where ev.StartDate = DateTime.Now
                  select ev).ToList();

List<Event> todaysTeamEvents = (from ev in allMarkTeamEvents
                  where ev.StartDate = DateTime.Now
                  select ev).ToList();

IEnumerable<Event> mergedEvents = todaysCorpEvents.Union(todaysTeamEvents);

var todaysAuditoriumEventTitles = from ev in mergedEvents
                  where ev.Location.Contains("Auditorium")
                  select new { ev.Title };

foreach (var eventTitle in todaysAuditoriumEventTitles)
{
    Console.WriteLine(eventTitle.Title);
}

Два объекта IList<T>должны иметь одинаковый параметр типа.

Шаг 7 (дополнительный): объединение результатов из нескольких источников данных

Метод, использовавшийся в шаге 7 для слияния данных из нескольких списков SharePoint Foundation, можно применить для слияния данных из списков SharePoint Foundation с данными из других источников при условии, что записи из другого источника можно привести к классу, представляющему тип контента SharePoint Foundation. Например, предположим, что LINQ to SQL используется в коде для создания объекта IList<T> (где "T" это Client) с именем oldClients, а LINQ to SharePoint используется для создания объекта IList<T> (где "T" это Patient) с именем activePatients. Если свойства и другие члены класса Client относятся к подмножеству членов класса Patient, а соответствующие члены имеют идентичные сигнатуры, то можно выполнить слияние данных из источника SQL с данными из списка SharePoint Foundation, как показано в данном примере.

foreach (Patient patient in activePatients)
{
    oldClients.Add((Client)patient);
}

Еще лучше для слияния данных от различных поставщиков LINQ использовать для них одинаковый класс типов элементов. Это возможно благодаря тому, что можно поместить декоративные элементы атрибута, необходимые для LINQ to SharePoint и для другого поставщика LINQ, в одно объявление класса. В следующем примере показана подпись объявления класса, которое дополнено атрибутом ContentTypeAttribute LINQ to SharePoint и атрибутом TableAttribute LINQ to SQL.

[ContentType(Name="Item", Id="0x01" List="Customers")]
[Table(Name = "Customers")]
public partial class Customer : Item
{
}

При использовании этих атрибутов для слияния данных из таблицы SQL с данными из списка SharePoint Foundation приведение типов не требуется.

См. также

Ссылка

SPMetal

ColumnAttribute

ContentTypeAttribute

DataContext

EntityList<TEntity>

EntityRef<TEntity>

EntitySet<TEntity>

ICollection<T>

IEnumerable

IEnumerator

IList<T>

IQueryable<T>

SPContext

SPWebApplication

TableAttribute

Концепции

Идентификаторы типов контента

Неподдерживаемые запросы LINQ и двухуровневые запросы

Получение ссылок на сайты, веб-приложения и другие ключевые объекты

Практическое руководство. Запись в базы данных контента с помощью LINQ to SharePoint

Другие ресурсы

LINQ to Objects

.NET LINQ для реляционных данных (LINQ to SQL) (Возможно, на английском языке)

LINQ to XML (Возможно, на английском языке)

Web Parts Overview