Использование сравнения дат в LINQ при запросе службы SharePoint OData

ОТВЕТ: Перейдите ниже, чтобы найти мой ответ на этот вопрос.

Я пытаюсь использовать OData SharePoint 2010 из проекта ASP.NET MVC 3 с использованием LINQ. Я создал проект по умолчанию, используя шаблон проекта ASP.NET MVC 3 с механизмом просмотра Razor (VS 2010). Я добавил ссылку на службу, указывающую на мой сайт SharePoint 2010.

В методе Index моего HomeController (это всего лишь тестовый проект) я создал переменную для хранения контекста и установил для свойства Credentials переменной контекста текущие учетные данные по умолчанию.

Запрос ссылки, подобный следующему, работает нормально, и я могу использовать созданную переменную для доступа к любым данным:

var query = from a in context.Alerts
            select a;

Этот запрос просто получает все объявления из списка Оповещения на сайте SharePoint. В этом списке есть поля для заголовка, содержания, даты начала и даты истечения срока действия.

Когда я изменяю запрос на следующий, я не получаю ожидаемых результатов:

var query = from a in context.Alerts
            where (a.Begins < DateTime.Now)
            select a;

Этот запрос игнорирует временную составляющую даты. Например, если a.Begins содержит вчерашнюю дату и время, запрос возвращает объект AlertItem. Если, с другой стороны, a.Begins содержит дату и время с текущей датой (но более раннее время), сравнение возвращает false (а a.Begins == DateTime.Now возвращает true).

Если я сделаю следующее, второй запрос LINQ будет работать так, как ожидалось:

var query = (from a in context.Alerts
            select a).ToList();
var query2 = from q in query
             where (q.Begins < DateTime.Now)
             select q;

Что мне не хватает?


person RWGodfrey    schedule 26.08.2011    source источник


Ответы (5)


Для запроса Linq to SharePoint, который должен включать элемент времени DateTime, вы можете использовать TimeOfDay.

var next_slots = (from s in dc.HRDates
                  where
                  s.StartTime.HasValue &&
                  s.StartTime.Value.Date == appt.Value.Date &&
                  s.StartTime.Value.TimeOfDay == appt.Value.TimeOfDay
                  ...
person Des Owen    schedule 02.03.2016

Я не использовал OData SharePoint 2010. Однако при запросе к объектной модели SharePoint 2010 опубликованная вами аномалия является распространенным поведением, re: вы должны преобразовать запрос в список, прежде чем вы сможете запрашивать данные.

Типичный шаблон здесь таков:

var query = someSharePointQuery.ToList();

var results = query.Where(...).First(...);

Кажется странным, но именно так работает SP 2010.

person Metro Smurf    schedule 29.08.2011

Где-то используется DateTime.Today? Я сделал некоторое прототипирование с помощью LinqPad, и единственный способ продублировать ваши результаты - это если бы у меня был запрос, использующий «где (a.Begins ‹ DateTime.Today)»

Вот краткий набросок того, что вы описываете:

void Main()
{
    List<Alerts> alerts = new List<Alerts>();
    alerts.Add(new Alerts(DateTime.Now.AddDays(-1)));
    alerts.Add(new Alerts(DateTime.Now));

    var query = from a in alerts
                where (a.Begins < DateTime.Now)
                select a;
    foreach (var element in query)
    {
        Console.WriteLine(element.Begins);
    }

}
public class Alerts
{
    public DateTime Begins {get; set;}
    public Alerts(DateTime begins)
    {
        Begins = begins;
    }
}

Как я уже упоминал, единственным способом дублировать описанные вами результаты было изменение DateTime.Now на DateTime.Today в предложении where. Я бы просмотрел ваш код на предмет случайного использования неправильного метода DateTime.

Кроме того, я НАСТОЯТЕЛЬНО рекомендую использовать LinqPad для прототипирования ваших запросов Linq... Это может сэкономить ваше время, позволяя вам быстро перебирать код и выяснять, в чем ваши проблемные места. Кроме того, он стоит 50 долларов за Intellisense и другие премиальные функции.

person Joe    schedule 26.08.2011
comment
Нет. Он отлично работает, если НЕ выполняет запросы к источнику OData SharePoint 2010. Поведение, которое я описал, ТОЛЬКО при работе с источником OData SharePoint 2010. Но чтобы ответить на ваш вопрос; нет, я нигде не использую DateTime.Today. - person RWGodfrey; 26.08.2011
comment
Тот факт, что a.Begins == DateTime.Now ОЧЕНЬ странный... Кроме того, это звучит так, как будто у вас есть записи в памяти, т.е. в первом запросе предложение DateTime работает просто отлично... Интересно, есть ли какие-либо известные проблемы с OData вокруг DateTime? - person Joe; 26.08.2011
comment
До сих пор ЧАСЫ поисков в Bing/Google/Yahoo и БОЛЬШЕ ЧАСОВ проб и ошибок ничего не дали. Я надеюсь, что кто-нибудь, имеющий доступ к SharePoint 2010, проведет аналогичный тест и сообщит о своих результатах. - person RWGodfrey; 26.08.2011

Собрав воедино информацию из множества разных источников, ни один из которых не касался точных обстоятельств проблемы, с которой я столкнулся, я пришел к следующему выводу:

При запросе данных SharePoint с использованием объектной модели SharePoint и языка разметки совместных приложений (CAML) SharePoint по умолчанию не использует компонент времени элементов DateTime при выполнении сравнений. Чтобы указать SharePoint использовать компонент времени, вы должны включить свойство IncludeTimeValue = 'TRUE' в тип значения, как показано здесь:

<Where>
 <Eq>
  <FieldRef Name='Begins' />
  <Value Type='DateTime'  IncludeTimeValue='TRUE'>
   2008-03-24T12:00:00Z
  </Value>
 </Eq>
</Where>

Я нашел несколько сообщений в блогах, в которых упоминается ошибка в LINQ to SharePoint, из-за которой сгенерированный CAML выводится как:

<Where>
 <Eq>
  <FieldRef Name='dateTimeField'  IncludeTimeValue='TRUE' />
  <Value Type='DateTime'>
   2008-03-24T12:00:00Z
  </Value>
 </Eq>
</Where>

Обратите внимание, что IncludeTimeValue = 'TRUE' находится в элементе FieldRef, а не в элементе Value. Поскольку это неподходящее место для этого свойства, оно приводит к тому, что все запросы LINQ to SharePoint, выполняющие сравнения даты и времени, сравниваются только по компоненту даты.

Поскольку я наблюдаю точно такое же поведение при использовании служб данных LINQ и WCF для подключения к SharePoint, я могу только предположить, что под прикрытием службы данных LINQ/WCF создают тот же недопустимый CAML.

Решение (при условии, что я все еще хочу использовать службы данных LINQ/WCF) состоит в том, чтобы выполнить два запроса (как указано в исходном вопросе). Первый запрос LINQ извлекает данные списка из SharePoint и сохраняет их в списке. Второй запрос LINQ обрабатывает сравнение дат, чтобы получить только те данные, которые мне нужны.

Поскольку в моих конкретных обстоятельствах у меня может быть много записей в списке SharePoint, охватывающих большой промежуток времени, но меня будут интересовать только записи в определенный день или пару дней, я хотел найти способ не возвращать весь список в первый запрос.

Я остановился на сравнении ‹= и >=, чтобы приблизиться, а затем еще больше ограничил это во втором запросе. Итак, мои два запроса теперь становятся:

DateTime RightNow = DateTime.Now;
var query = (from a in context.Alerts
             where (a.Begins <= RightNow) && (a.Expires >= RightNow)
             select a).ToList();
var query2 = from q in query
             where q.Begins < RightNow) && (a.Expires > RightNow)
             select q;

Первый оператор LINQ вернет все элементы, которые меня в конечном счете интересуют; вместе с некоторыми, которыми я не являюсь (потому что он сравнивает только компонент даты даты и времени). Второй оператор LINQ еще больше сократит это до тех, которые меня интересуют.

person RWGodfrey    schedule 29.08.2011

Я могу подтвердить, что ошибка, связанная с SharePoint 2010 LINQ to SharePoint, которая не создает правильный CAML (добавление IncludeTimeValue='True' в FieldRef вместо Value), исправлена ​​накопительным обновлением SharePoint Foundation 2010 за октябрь 2013 года. Исправление можно загрузить с веб-сайта http://technet.microsoft.com/en-us/sharepoint/ff800847.aspx .

Та же ошибка существует и в SharePoint 2013, которая, как мне сообщила служба поддержки Microsoft, должна быть исправлена ​​в накопительном обновлении SharePoint Foundation 2013 за декабрь 2013 года, но я не могу это подтвердить. Мне сообщили, что исправление также развернуто в Office 365, но я не могу это подтвердить.

person Remco Blok    schedule 31.01.2014