Серверный Fetchxml возвращает разные результаты

Одна из наших процедур позволяет пользователям массово вставлять связанные записи, выбирая вид, а затем нажимая кнопку ленты. Форма сохранена, установлен флаг, и плагин выполняет свою работу.

Мы используем подсетку с селектором представлений, чтобы пользователи могли выбирать или создавать свои собственные представления на лету. После выбора вида отображается количество результатов (при условии, что это 5k).

Когда плагин запускает ту же серверную часть fetchxml (Получить запрос пользователя или сохраненный запрос, затем Получить + выражение FetchExpression), результаты изменяются. Мы получаем не только различное количество записей, но и некоторые записи разные.

Мы пришли к выводу, что проблема связана с часовыми поясами. Некоторые фильтры включали операторы "on-or-after" вместе со значениями даты. Пример:

<filter type="and">
  <condition attribute="modifiedon" operator="on-or-after" value="2011-01-01" />
  <condition attribute="modifiedon" operator="on-or-before" value="2011-12-31" />
</filter>

Плагин работал от имени администратора. Изменение пользователя плагина не имеет никакого эффекта - как будто текущий часовой пояс пользователя не учитывается при извлечении записей из CRM с использованием выражения FetchExpression.

Как я могу гарантировать, что выражение fetchxml возвращает одинаковые результаты на стороне клиента и на стороне сервера?

Вероятно, связано: поток MSDN.

Спасибо за ваше время.

Редактировать: следуя совету Дэрила, я запустил трассировку SQL. Результаты удивительны. Даты корректно смещаются для запросов на стороне клиента (запускаются из CRM, т.е. расширенного поиска) - это означает, что fetchxml правильно переведен с использованием настроек часового пояса пользователя. Это не происходит для одного и того же запроса на стороне сервера; выходной SQL содержит фильтры даты "как есть" без смещения часового пояса. Я предположил, что один и тот же перевод произошел независимо от источника контекста выполнения запроса.

Редактировать 2: флаг в скрытой области кода (мой последний способ отладки) не позволял плагину создавать экземпляр службы в контексте работающего пользователя. Теперь все работает нормально. Спасибо всем за ваше время и вашу помощь, это очень ценится.

1 ответ

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

Собственная находка CRM Advanced будет искать часовой пояс текущего пользователя и преобразовывать его в любое время в расширенный поиск в UTC перед выполнением запроса SQL. Ваш плагин управления должен будет сделать то же самое. Это шаги, которые вам нужно выполнить, прежде чем помещать критерии в выражение Fetch Xml / Linq Expression / Query Expression.

  1. Получите UserSetting.TimeZoneCode пользователя через его SystemUserId.
  2. Найдите TimeZoneDefinition.StandardName для TimeZoneCode из шага 1
  3. Вызов TimeZoneInfo.FindSystemTimeZoneById(), передающий стандартное имя из шага 2 (вы можете объединить шаги 1 и 2 в один запрос, но я предпочитаю кэшировать результаты шага три, используя входные данные шага 1 для небольшого улучшения производительности. использовать словарь с TimeZoneCode в качестве ключа и TimeZoneInfo в качестве значения)
  4. Используйте эту функцию, чтобы получить значение UTC для времени, которое вы собираетесь использовать в запросе плагина:

public static DateTime ConvertTimeToUTC(DateTime time, TimeZoneInfo timeZone)
{
    if (time.Kind != DateTimeKind.Unspecified)
    {
        // If the DateTime is created with a specific time zone(ie DateTime.Now), getting the offset will
        // blow chow if it isn't the correct time zone:
        // The UTC Offset of the local dateTime parameter does not match the offset argument.
        //Parameter name: offset

        // This quick check will recreate the serverLocal time as unspecified

        time = new DateTime(
            time.Year,
            time.Month,
            time.Day,
            time.Hour,
            time.Minute,
            time.Second,
            time.Millisecond);

    }
    var offest = new DateTimeOffset(time, timeZone.GetUtcOffset(time));
    return offest.UtcDateTime;
}
Другие вопросы по тегам