AXForum  
Вернуться   AXForum > Блоги > CRM, SharePoint и Черная Магия
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

Добро пожаловать в мой блог! Изначально он не задумывался как блог CRM разработчика, но жизнь сама внесла нужные коррективы. Тут я публикою все свои наблюдения относительно обозначенных в заголовке систем. Если Вы найдете в нем что-то интересное для Вас, как для заказчика, то буду рад сотрудничать с Вами! В моей компетенции 100% задач по MS CRM 3.0/4.0/2011:
  • Консалтинг
  • Проектирование
  • Разработка
  • Обучение


MVP 2010, 2011
Оценить эту запись

Особенности выборки по Id и ObjectId в OrganizationSericeContext

Запись от Артем Enot Грунин размещена 28.10.2016 в 09:39
Теги bug, linq

Недавно я (впрочем не я первый) обнаружил интересный баг при работе с "контекстом" в стандартной клиентской сборке CRM. Представители "старой школы", такие как я, не привыкли пользоваться подобными инструментами, однако использование LINQ синтаксиса запросов несомненно востребовано современными программистами.

В чем, собственно суть? Предположим, вы хотите получить только идентификаторы записей, которые удовлетворяют некоторому условию. Упрощенно, ваш запрос будет выглядеть примерно вот так
X++:
                using (OrganizationServiceContext context = new OrganizationServiceContext(proxy))
                {
                    var account = context.CreateQuery<Account>()
                        .Where(a => a.Name != null)
                        .Select(a => new Account() { Id = a.Id }).FirstOrDefault();
 
                    Console.Out.WriteLine("Id = a.Id {0}", account.Attributes.Count);
                }
Сюрпризом будет вывод программы. Было вычитано 57 атрибутов! Иными словами, все атрибуты, а не только те что я заказывал.

При использовании классического подхода к выборке данных из системы посредством QueryBase, мы в обязательном порядке должны указать ожидаемый набор атрибутов в CollumnSet. Тем не менее, система имеет свои представления касательно того что будет возвращено клиенту. В наборе атрибутов каждой запрошенной записи, в обязательном порядке будет ее идентификатор (и ряд других системных полей), например, accountid, но не будет всех полей, значения которых оказались пустыми. Важно понимать, что OrganizationSericeContext не создает под собой чего-то принципиально нового: наш LINQ запрос будет транслирован LINQ-провайдером в стандартный запрос-наследник QueryBase (по слухам генерируется Fetch-запрос). Однако, он, почему-то генерирует не пустой CollumnSet а CollumnSet(true), который вынужден вернуть все атрибуты.

Идем дальше! Давайте попробуем использовать немного другой запрос:
X++:
                using (OrganizationServiceContext context = new OrganizationServiceContext(proxy))
                {
                    var account = context.CreateQuery<Account>()
                        .Where(a => a.Name != null)
                        .Select(a => new Account() { AccountId = AccountId }).FirstOrDefault();
 
                    Console.Out.WriteLine("Id = a.AccountId.Value {0}", account.Attributes.Count);
                }
В результате мы получим 1 атрибут, как и договаривались. Для генерации классов раннего связывания использовалась стандартная утилита CrmSvcUtil 7.1.0001.3108 (4.0.30319.42000 – версия указанная в начале файла с кодом). При этом, если мы посмотрим на определение свойств AccountId и Id в генерированном коде, мы не увидим значимых отличий:
accountid
X++:
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("accountid")]
public System.Nullable<System.Guid> AccountId

[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("accountid")]
public override System.Guid Id
Оба свойства маппированы на один и тот же атрибут, следовательно должны транслироваться в одинаковые запросы при преобразовании лямбда выражения в Fetch запрос. Тем не менее, разница на лицо.

Попробовав все возможные комбинации Id и AccountId в запросе, мы получим следующий вывод программы:

X++:
AccountId = a.Id 57
Id = a.Id 57
Id = a.AccountId.Value 1
AccountId = a.AccountId 1
Иными словами, важно чтобы в правой части выражения всегда был AccountId.

Мне не удалось получить какой-либо внятный ответ от производственной группы посредством возможностей MVP канала. Однако выяснилось, что проблема известная, хотя и не критическая: http://crmtipoftheday.com/2014/09/09/bad-id-bad/.

Второй интересный момент. Вы всегда должны заказывать AccountId в выражении select, иначе это свойство не будет инициализировано в записи Account. Технически, его значение будет где-то там - внутри контекста, но использовать его вы не сможете.

Предупрежден - значит вооружен. Не жалейте буквы и пишите полные названия идентификаторов!
Размещено в CRM
Просмотров 22575 Комментарии 0
Всего комментариев 0

Комментарии

 


Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 17:03.