AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 07.04.2011, 13:22   #1  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Как правильно хранить статичный набор начальных данных в классах?
Вопрос: Как правильно хранить статичный набор начальных данные в классах?

=========================
Дисклаймер 1:
хотелось бы обсудить не технические детали, а подходы к программированию в Аксапте. Обсудить плюсы и минусы.

Дисклаймер 2:
Специально возьму пример не из области учета, а из области программирования низкого уровня (чтобы не начинать обсуждение в стиле "сделай по-другому", "используй другой функционал").

Дисклаймер 3:
я специально спрашиваю про классы, а не таблицы.
с таблицами - никаких вопросов. Кроме того, что таблицы надо создавать и инициализировать отдельно.
а вот классы:
3.1. могут "жить" и на клиенте, и на сервере.
3.2. могут наследовать - первоначальные данные могут переопределяться/добавляться в потомках
3.3. живут в памяти

=========================
пусть я создаю какой-то системный класс, который обновляет Query.
в классе статически прописан Query, в потомках Query может меняться.

и есть начальные данные, которые прописывает программист (НЕ ПОЛЬЗОВАТЕЛЬ):
набор пар <таблица, поле>
именно на эти поля и накладывается фильтр классом.

Дисклаймер 4: таблиц может быть несколько, полей может быть несколько, поля могут иметь расширенный код (например, Dimension[2])

Дисклаймер 5: хотелось бы обсудить хранение не только пар, но и троек/четверок/пятерок значений. Главное, что эти пары/тройки/четверки/и.т.д. - однородны по своей структуре.

============================
В С++ я бы написал что-то вроде
struct { int64 refTable; int refField } myStruct;
set<myStruct> myStructSet = ( {Table1, Field1}, {Table2, Field2} );
и получил бы одновременно и контроль типов, и легкость инициализации.

========================
Вопрос: а как лучше хранить такие статические наборы значений?

Что можно использовать в Аксапте:
Вариант 1.1
= контейнер контейнеров:
= пример [ [table1, field1], [table2, field2] ]
=== плюсы:
======= легко инициализировать
======= легко добавлять/изменять значения в наборе в классах потомках
=== минусы:
======= никакой типизации и никакой проверки типов на этапе компиляции

Вариант 1.2
= контейнер структур (struct):
= пример
new struct1 = new Struct(Types::Int64, Types::Integer);
new struct2 = new Struct(Types::Int64, Types::Integer);
... [ struct1, struct2 ]

=== плюсы:
======= хоть какая то типизация
=== минусы:
======= инициализировать уже неудобно
======= нет проверки заполненности полей в структуре

Вариант 1.3
= контейнер собственных классов (или подходящих. В данном случе можно использовать DictField)
= пример [ new myPairClass(table1, field1), new myPairClass(table2, field2) ]
=== плюсы:
======= полный контроль над целостностью и валидностью данных
======= в потомках легко добавлять/изменять наборы
=== минусы:
======= нужно программировать свой класс - трудоемко

Вариант 2.1
set of контейнеров (множество контейнеров)

Вариант 2.2
set of struct (множество структур)

Вариант 2.3
set of myPairClass (множество собственных классов)

Вариант 3.1
list of контейнеров (множество контейнеров)

Вариант 3.2
list of struct (множество структур)

Вариант 3.3
list of myPairClass (множество собственных классов)

другие способы?
========================

Какой способ вы предпочитаете?
Почему? Какие плюсы и минусы у разных способов?
Какие примеры хранения наборов данных есть в стандартной Аксапте?

========================
Например, в Аксапте есть FieldList.
Этот класс хранит список полей в одной (!) таблице.
Этот класс хранит список полей в контейнере. (со всеми вытекающими по контролю типов)
Этот класс пытается сам предоставить интерфейс доступа через find.
Этот класс не предоставляет enumerator'а для своего содержимого
В общем, как-то не айс.

Вопрос: Как правильно хранить статичный набор начальных данные в классах?
__________________
полезное на axForum, github, vk, coub.

Последний раз редактировалось mazzy; 07.04.2011 в 13:29. Причина: добавил оговорку про DictField. Да, можно использовать и его. Но хотелось бы обсудить более общий вариант.
Старый 07.04.2011, 13:34   #2  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
а может вообще не парится с такими начальными данными, а просто делать специализированные методы, в которых статически написано что и с какими полями нужно делать?
__________________
полезное на axForum, github, vk, coub.
Старый 07.04.2011, 13:41   #3  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,907 / 5717 (196) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Я бы еще добавил - создать временную таблицу правильной структуры (с добавлением ключа или просто индекса как в массиве) и засунуть ее в RecordSortedList.
Мне кажется что это будет самым компактным (в плане занимаемой памяти) и самым быстрым (сопоставимым с Map-ами по времени доступа) вариантом. Хотя обосновать не смогу...
Старый 07.04.2011, 14:02   #4  
George Nordic is offline
George Nordic
Модератор
Аватар для George Nordic
Злыдни
 
4,479 / 1250 (50) ++++++++
Регистрация: 17.12.2003
Адрес: Moscow
Записей в блоге: 9
Цитата:
Сообщение от mazzy Посмотреть сообщение
а может вообще не парится с такими начальными данными, а просто делать специализированные методы, в которых статически написано что и с какими полями нужно делать?
Это как обычно? Типа:
X++:
int parmFunParm(int _funParm = funParn)
{
    ;
    funParm = _funParm;
    return funParm;
}
Старый 07.04.2011, 14:09   #5  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,438 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Как развёрното замечено в первом посте, у каждого подхода есть и плюсы и минусы. А следовательно выбор того или иного способа реализации должен производится с учётом специфики конкретной задачи. Что важнее, память, скорость, надёжность, простота, масштабируемость?

Если сравнивать аксапту с другими системами. То я бы отметил, что наличие в аксапте встроенных средств работы с табличными курсорами является ценным преимуществом, от которого не нужно отказываться.
Старый 07.04.2011, 14:18   #6  
CDR is offline
CDR
MCTS
MCBMSS
 
236 / 175 (6) ++++++
Регистрация: 27.11.2003
Контейнер не может содержать в себе классы, т.к. сам не является классом.
Контейнер относится к value-типам (как str, int, real), а не к reference-типам (класс, запись).
Следовательно структура контейнера создается в памяти только один раз при его инициализации. Последующие изменения содержимого контейнера реализованы, как создание нового контейнера и копирование значений из старого в новый.
Отсюда крайне не рекомендуется использовать решения вроде заполнения контейнера в цикле. Но полезно использовать, например, для передачи параметров между клиентом и сервером.
__________________
Dynamics AX Experience
За это сообщение автора поблагодарили: mazzy (2), S.Kuskov (1).
Старый 07.04.2011, 14:45   #7  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от fed Посмотреть сообщение
Я бы еще добавил - создать временную таблицу правильной структуры (с добавлением ключа или просто индекса как в массиве) и засунуть ее в RecordSortedList.
Ее чертовски неудобно инициализировать и отлаживать.
К тому же, ее в АОТ создавать нужно... На каждый чих не насоздаешься.
Но согласен, что можно. Особенно для набора нескольких разнородных параметров.

Цитата:
Сообщение от George Nordic Посмотреть сообщение
Это как обычно? Типа
Параметров может быть несколько.
В данном случае может быть несколько пар <таблица/поле>.
Что должна возвращать функция? Куда? Как работать с этим

Цитата:
Сообщение от CDR Посмотреть сообщение
Контейнер не может содержать в себе классы, т.к. сам не является классом.
Контейнер относится к value-типам (как str, int, real), а не к reference-типам (класс, запись).
опаньки. спасибо.
значит способы 1.2, 1.3 отпадают.

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

остальные способы приводят к появлению кучи кода по инициализации начальных данных...

============================
как удобнее, как правильнее хранить статичный набор начальных данных?
__________________
полезное на axForum, github, vk, coub.
Старый 07.04.2011, 14:46   #8  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Напомню, что я мечтал бы о чем-то подобном - кратко, понятно и легко для восприятия программистом.
Цитата:
Сообщение от mazzy Посмотреть сообщение
struct { int64 refTable; int refField } myStruct;
set<myStruct> myStructSet = ( {Table1, Field1}, {Table2, Field2} );
и получил бы одновременно и контроль типов, и легкость инициализации.
__________________
полезное на axForum, github, vk, coub.
Старый 07.04.2011, 14:54   #9  
CDR is offline
CDR
MCTS
MCBMSS
 
236 / 175 (6) ++++++
Регистрация: 27.11.2003
Цитата:
Сообщение от mazzy Посмотреть сообщение
из способов с удобным для программиста написанием кода
остается только контейнер контейнеров.
А макросы не вариант?
__________________
Dynamics AX Experience
Старый 07.04.2011, 14:56   #10  
belugin is offline
belugin
Участник
Аватар для belugin
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,622 / 2925 (107) +++++++++
Регистрация: 16.01.2004
Записей в блоге: 5
имхо тут компромисс между легкостью инициализации и статическим контролем. То что хочет Маззи можно получить комбинацией вот этого тула:
http://www.axaptapedia.com/Generate_...hods_extension
И паттерна expression builder.

В простых случаях я предпочитаю контейнер контейнеров, расставиив ассерты и документировав.

Сложных случаев я пока не делал.
За это сообщение автора поблагодарили: mazzy (2).
Старый 07.04.2011, 15:00   #11  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от belugin Посмотреть сообщение
имхо тут компромисс между легкостью инициализации и статическим контролем.
угу.

Цитата:
Сообщение от belugin Посмотреть сообщение
То что хочет Маззи можно получить комбинацией вот этого тула:
http://www.axaptapedia.com/Generate_...hods_extension
И паттерна expression builder.
слишком сложно. и контроль замороченный.

Цитата:
Сообщение от belugin Посмотреть сообщение
В простых случаях я предпочитаю контейнер контейнеров, расставиив ассерты и документировав.
Вот-вот... Я тоже. Но неаккуратненько как-то...
Вдруг чего не знаю?
__________________
полезное на axForum, github, vk, coub.
Старый 08.04.2011, 01:59   #12  
Ievgenii is offline
Ievgenii
Участник
Аватар для Ievgenii
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
 
111 / 113 (4) +++++
Регистрация: 21.09.2008
Адрес: Copenhagen, Denmark
Цитата:
а может вообще не парится с такими начальными данными, а просто делать специализированные методы, в которых статически написано что и с какими полями нужно делать?
Иммено. в construct и newFromXXX методах

Вариант 3.3
Стратегически рано или позно мы будем в С# мире, а тут понятие контейнер чуждо и не особо понятно что с ним делать. Учитывая что ваш код может использоваться сторонними приложениями через разные прокси и обвертки – классы предпочтительней. С другой стороны есть проблемы с производительностью при создании классов в аксапте. В 6-ке лутше – но все равно, не бесплатно.
Кроме того классы проще тестировать, проще создать мок обьекты чем с контейнерами, проще читать код (ИМХО)
Также проще его поддерживать людям которые приходят с другий областей.
__________________
Thx,
Ievgenii Korovin| Dynamics Ax SCM| Microsoft Corp| http://blogs.msdn.com/DynamicsAxSCM/

Последний раз редактировалось Ievgenii; 08.04.2011 в 02:03.
За это сообщение автора поблагодарили: mazzy (2).
Старый 08.04.2011, 07:32   #13  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от Ievgenii Посмотреть сообщение
Иммено. в construct и newFromXXX методах
Хм...

Цитата:
Сообщение от Ievgenii Посмотреть сообщение
Вариант 3.3
Стратегически рано или позно мы будем в С# мире
так то оно так...
но ведь есть еще вопросы сериализации/десериализации, передачи клиент/сервер.
сейчас pack/unpack работают через контейнер.

а как со списками/мапами/сетами?
или забить?

==============
получается, что в варианте 3.3 надо:
1. создать свой класс-примитив для хранения значений
2. писать что-то многословное вида { storage = new list(Types::Classes); storage.add(new MyPair(...)); ... }
3. поизвращаться с pack/unpack

хотя я полностью согласен, что enumerat'ором потом удобно обрабатывать.
__________________
полезное на axForum, github, vk, coub.
Старый 08.04.2011, 14:42   #15  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,691 / 1195 (43) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Ну, лично я начинал бы инициализацию с контейнера контейнеров. А вот дальше все зависит от того, как предполагается использовать этот статический набор.

Можно ведь следующим шагом перегнать контейнер-контейнеров в нужный тип хранилища. В ту же временную таблицу, например. Имею в виду, нечто вроде такого кода инициализации

X++:
Container   conValue;
int                nextI;
TmpTable    tmpTable;
;

// Сами данные
conValue = [
                     [Table1, Field1]
                    ,[Table2, Field2]
                    ,[Table3, Field3]
                    ];

// Конвертация данных в необходимое представление
for (nextI = 1; nextI <= conLen(conValue); nextI++)
{
    tmpTable.refTableId = conPeek(conPeek(conValue,nextI), 1);
    tmpTable.refFieldId = conPeek(conPeek(conValue,nextI), 2);
    tmpTable.doInsert();
}


Другими словами просто физически разделяем процесс инициализации данных от их конвертации в удобный для дальнейшего использования вид.
За это сообщение автора поблагодарили: mazzy (2).
Старый 10.04.2011, 11:46   #16  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,311 / 3547 (125) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
Примеры в коде:
1. \Classes\smmSalesManagementQueries, метод defaultQuery и метод, из которого он вызывается createDefaultQueries
В интерфейсе: \CRM\Периодические операции\Управление продажами\Статистика продаж, кнопка Функции - Импорт запроса по умолчанию.
В методе defaultQuery прописан весь набор начальных данных, который делится между собой макросами, прописанными в ClassDeclaration этого класса. В результате - каждая итерация цикла вставки в таблицу по сути прописана в коде и каждая запись промаркирована своим макросом.
В конечном счете заполняется таблица.
Данный пример тянется с версии 3.0 точно. Раньше не знаю.

2. \Data Dictionary\Tables\BITimePeriodsMDX\Methods\importFromAOT и методы importFromXml и fromXml.
В интерфейсе: \Администрирование\Настройка\OLAP\Периоды времени, кнопка Импорт-Импорт из АОТ.
Здесь начальные данные вытаскиваются из ресурсов (\Resources\BITimePeriodsMDX), в которых был сохранен XML-файл (и кстати, обратно могут быть сохранены туда же). В общем-то идея хранения данных в XML-файле сродни контейнеру контейнеров, просто с использованием другой технологии (в памяти торчит объект xmlDocument - как аналог контейнера контейнеров). Плюс от использования ресурсов - возможность переноса данных (которые прописываются в коде) вместе с приложением. При этом храня сами данные не в коде.
Результат импорта из ресурсов записывается в табличку BITimePeriodsMDX
Данный пример присутствует начиная с DAX 2009

Пока навскидку еще не приходят примеры.
__________________
Возможно сделать все. Вопрос времени

Последний раз редактировалось sukhanchik; 10.04.2011 в 11:52.
За это сообщение автора поблагодарили: mazzy (2).
Старый 10.04.2011, 12:27   #17  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,311 / 3547 (125) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
Еще примеры вспомнил.
Классы NumberSeqReference* (автоматическое заполнение таблицы на закладке Номерные серии).
Классы ReleaseUpdate* (автоматическое формирование списка пакетных заданий для скриптов - обновления версии при прохождении контрольного списка обновления).

Суть: есть некий набор (небольшой) параметров, каждый из которых умещается в своем поле таблички (для номерных серий - см метод load, в котором каждый из нас по сути прописывает заполнение этих полей).
Дальше выполняется метод create(), который уже "по-умному" дополняет запись и вставляет/обновляет ее по мере необходимости (допустим, мы изменили HelpText в методе load - так несмотря на то, что запись в табличке NumberSequenceReference уже существует - то HelpText будет обновлен).

При этом важно - что каждый наследник (в обоих примерах) по сути по-своему заполняет нужную табличку. И проблема типизации отсутствует. А табличка всегда существует в БД и заполняется по мере обращения к ней (открытия формы параметров в каждом модуле или инициализация всех наследников сразу для скриптов обновлений)
__________________
Возможно сделать все. Вопрос времени

Последний раз редактировалось sukhanchik; 10.04.2011 в 12:30.
Старый 10.04.2011, 13:59   #18  
Андре is offline
Андре
Moderator
Сотрудники компании GMCS
 
2,375 / 464 (20) +++++++
Регистрация: 03.12.2001
Я обычно выбираю разновидность варианта 1.3 - класс, содержащий Set/Map/List других классов. То есть, работаю с двумя сущностями - Entity и EntityCollection.

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

Как минимум в классе Entity я добавлю аналог метода toString(), для удобного отображения его содержимого в infolog-е, и методы set()/get(), поставив точку останова на которых другие разработчики смогут контролировать работу с этими классами.

Кроме того, в данном варианте, код работающий с этими классами выглядит гораздо понятнее и проще, за счет того, что все низкоуровневые манипуляции с данными (conpeek/conpoke/работу с индексами/проверки) я прячу в методы этих классов, имеющие говорящие имена и сразу понятные разработчику.

В будущем иногда бывает полезно создать класс EntityIterator для того, чтобы перебирать элементы коллекции. Кроме того, интерфейс для работы с моей коллекцией в этом случае будет выглядеть схожим образом с интерфейсом стандартных коллекций и их итераторов.
Старый 10.04.2011, 14:47   #19  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от Андре Посмотреть сообщение
Я обычно выбираю разновидность варианта 1.3 - класс, содержащий Set/Map/List других классов. То есть, работаю с двумя сущностями - Entity и EntityCollection.
Entity на базе чего-то стандартного?
или полностью свое?
__________________
полезное на axForum, github, vk, coub.
Старый 10.04.2011, 14:50   #20  
Андре is offline
Андре
Moderator
Сотрудники компании GMCS
 
2,375 / 464 (20) +++++++
Регистрация: 03.12.2001
В зависимости от обстоятельств. Но как правило это обычный класс аксапты в ClassDeclaration которого объявлена переменная типа Set, Map или List. В общем то, можно и наследоваться, но как по мне, так такой вариант приводит к слишком большой зависимости между объектами.
Теги
как правильно

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Загрузка начальных данных MIVura DAX: Прочие вопросы 1 31.03.2009 14:52
Набор данных на лету HorrR DAX: Программирование 15 26.09.2008 15:21
Прогноз роста базы данных и выбор топологии системы, Как правильно расчитать возможный рост sergeypp DAX: Администрирование 0 05.12.2006 16:55
Введение в Аксапту Роман Кошелев DAX: Прочие вопросы 0 18.12.2001 14:00

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

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