Некоторое время назад вышел очередной релиз нашего проекта с открытым исходным кодом Camlex.Net. В версию 3.2 была добавлена достаточно интересная функциональность, о которой я хотел бы рассказать в этой статье.
Сначале несколько слов о том, что такое Camlex и как он помогает в разработке. Если вы работаете с Sharepoint, то скорее всего сталкивались с необходимостью писать CAML запросы. CAML (Collaborative Application Markup Language) — специальный язык, используемый в Sharepoint для различных целей. Одно из его применений — написание Sql-подобных запросов для выборки данных из листов и библиотек документов. Например, для того, чтобы выбрать все элементы из списка, в поле Title которых указано значение «Meeting», а в Description — «Sharepoint», нужно использовать следующий запрос:
<Where>
<And>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">Meeting</Value>
</Eq>
<Eq>
<FieldRef Name="Description" />
<Value Type="Text">Sharepoint</Value>
</Eq>
</And>
</Where>
Если вам понадобится добавить больше условий к запросу, вам придется перестраивать все Xml-дерево (в CAML элементы And и Or могут иметь только 2 операнда). Кроме того, если вы работаете со строками, компилятор не помогает вам, отлавливая ошибки на этапе компиляции.
С помощью Camlex вы можете написать этот запрос, используя лямбда выражения на C#:
string query = Camlex.Query()
.Where(x => (string)x["Title"] == "Meeting" && (string)x["Description"] == "Sharepoint").ToString();
Кроме этого в Camlex-е много полезных вещей для прикладных разработчиков. Больше деталей можно посмотреть в другой статье на Хабре, которая была опубликована около 2-х лет назад, когда мы выпустили первую версию проекта (мы — это я и Владимир Тимашков): Использование лямбд для построения CAML-запросов в SharePoint'е, а также в моем блоге статьи с меткой Camlex.Net: http://sadomovalex.blogspot.fi/search/label/Camlex.NET.
Теперь непосредственно к теме поста. Около полугода назад мы добавили достаточно нетривиальную функциональность в проект: реверс-инжиниринг CAML-а. Она позволяет разбирать строковые запросы и содавать на их основе дерево выражений (expression tree). С помощью реверс-инжиниринга мы запустили бесплатный онлайн сервис http://camlex-online.org, с помощью которого можно конвертировать строковые CAML-запросы в C# код. Т.е. получился Camlex наоборот. Если интересны детали, генерация C# кода из дерева выражений сделана с помощью другого проекта с открытым кодом ExpressionToCode (правда немного допиленного нами).
Недавно мы получили несколько запросов: разработчики спрашивали, можно ли добавлять дополнительные условия к существующим CAML-запросам? Задача показалась мне интересной и с учетом того, что у нас уже есть реверс-инжиниринг, вполне выполнимой. В версию 3.2 была добавлена эта функциональность. Теперь можно использовать Camlex вместе с инструментами, которые работают с обычными строковыми запросами, расширять их с помощью Camlex-а и передавать результат назад в виде строки.
Давайте посмотрим пример. Представим, что у нас есть следующий запрос:
<Where>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">Sharepoint</Value>
</Eq>
</Where>
он вернет нам элементы с заголовком «Sharepoint». Допустим, что нам надо добавить еще одно условие к запросу, чтобы также получить элементы с заголовком «Office». Теперь это можно сделать так:
string query = Camlex.Query().WhereAny(existingQuery, x => (string)x["Title"] == "Office").ToString();
т.е. просто передаем строковый запрос и новое условие в виде лямбда выражения. Результат будет следующим:
<Where>
<Or>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">Sharepoint</Value>
</Eq>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">Office</Value>
</Eq>
</Or>
</Where>
Можно также добавлять сразу несколько условий к более сложным строковым запросам. Вот список методов, которыми был расширен интерфейс IQuery:
public interface IQuery
{
// ...
IQuery WhereAll(string existingWhere, Expression<Func<SPListItem, bool>> expression);
IQuery WhereAll(string existingWhere, IEnumerable<Expression<Func<SPListItem, bool>>> expressions);
IQuery WhereAny(string existingWhere, Expression<Func<SPListItem, bool>> expression);
IQuery WhereAny(string existingWhere, IEnumerable<Expression<Func<SPListItem, bool>>> expressions);
IQuery OrderBy(string existingOrderBy, Expression<Func<SPListItem, object>> expr);
IQuery OrderBy(string existingOrderBy, Expression<Func<SPListItem, object[]>> expr);
IQuery OrderBy(string existingOrderBy, IEnumerable<Expression<Func<SPListItem, object>>> expressions);
IQuery GroupBy(string existingGroupBy, Expression<Func<SPListItem, object>> expr);
IQuery GroupBy(string existingGroupBy, Expression<Func<SPListItem, object[]>> expr);
}
Т.е. можно расширять не только условия запросов (WhereAll и WhereAny), но и OrderBy и GroupBy. Также можно расширять ViewFields, но эти методы находятся в другом интерфейсе, т.к. не являются частью общей цепочки вызовов при конструировании запроса.
Рассмотрим более сложный пример. Добавим к строковому запросу, который мы получили выше, сразу два условия: выбрать также элементы, в которых число участников больше 1 и статус не пустой:
string existingQuery =
"<Where>" +
" <And>" +
" <Eq>" +
" <FieldRef Name="Title" />" +
" <Value Type="Text">Sharepoint</Value>" +
" </Eq>" +
" <Eq>" +
" <FieldRef Name="Title" />" +
" <Value Type="Text">Office</Value>" +
" </Eq>" +
" </And>" +
"</Where>";
var query = Camlex.Query().WhereAny(existingQuery,
x => (int)x["Participants"] > 1 && x["Status"] != null).ToString();
В результате получим:
<Where>
<Or>
<And>
<Gt>
<FieldRef Name="Participants" />
<Value Type="Integer">1</Value>
</Gt>
<IsNotNull>
<FieldRef Name="Status" />
</IsNotNull>
</And>
<And>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">Sharepoint</Value>
</Eq>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">Office</Value>
</Eq>
</And>
</Or>
</Where>
Т.е. Camlex берет всю работу по формированию достаточно сложного CAML-запроса на себя.
В заключение хотелось сказать, что Camlex — это проект, который развивается из запросов сообщества. Если у вас есть идеи и предложения по добавлению новой функциональности, пишите их на сайте проекте в разделе Обсуждения. Стараемся отвечать на все вопросы и по мере возможностей добавлять новую функциональность.
Автор: sadomovalex