Camlex 3.2: реверс-инжиниринг CAML и добавление условий к строковым запросам в Sharepoint с помощью лямбда выражений

    Некоторое время назад вышел очередной релиз нашего проекта с открытым исходным кодом 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 — это проект, который развивается из запросов сообщества. Если у вас есть идеи и предложения по добавлению новой функциональности, пишите их на сайте проекте в разделе Обсуждения. Стараемся отвечать на все вопросы и по мере возможностей добавлять новую функциональность.
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 2
    • +1
      Напишите в заголовке, что это про шарепоинт. А то из RSS создаётся впечатление, что статья связана c каким-то языком из группы ML.

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.