| 
	 | 
| 
			
			 | 
		#1 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
			
			
			Приведение типов для таблиц ax2012
			 
			
			Всем привет. 
		
		
		
		
		
		
		
	Коллеги, ковыряюсь с наследованием табличек. Возможно ли приводить тип таблички к типу не определенному на этапе компиляции? Штатный способ X++: common as tableName Сделал вот такой способ X++: // приводит табличный буфер к заданному типу // работает аналогично оператору as // отличие в том, что 2-м операндом принимает переменную // т.е. на этапе компиляции тип может быть неизвестен public static Common as( Common _common, TableName _tableName) { anyType anyBuffer; Common ret; ; if (tableName2id(_tableName)) { // anyBuffer = _common as _tableName anyBuffer = new SysDictTable(tableName2id(_tableName)).makeRecord(); anyBuffer = _common; ret = anyBuffer; } return ret; } Но он немного хакерский. Есть что-то более документированное ?  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: sukhanchik (6). | |
| 
			
			 | 
		#2 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Если оно неопределенность на этапе компиляции, то это не привидение типов. В рантайме он уже своего собственного типа. Чего вы хотите добиться?
		 
		
		
		
		
		
		
		
	 | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: Logger (1). | |
| 
			
			 | 
		#3 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
Изначально я пытался порешать вот эту проблему: http://sashanazarov.blogspot.com/201...-fails-on.html Для решения написал метод в Global (он как раз решает проблему описанную в блоге sashanazarov - позволяет обойти баги ядра) X++: // обходим баг вызова orig() для табличек с наследованием // [url]http://sashanazarov.blogspot.com/2014/01/dynamics-ax-2012-orig-method-fails-on.html[/url] // [url=http://axforum.info/forums/showthread.php?p=328140#post328140]Опасный orig[/url] public static anyType origFieldValue_MRC(Common _common, FieldId _fieldId) { TableId tableId; TableId tableId4Field; FieldName fieldName; Common commonCasted; Common commonOrig; DictTable dictTable; anytype ret; ; if (_common.RecId == 0) { // обходим баг // [url=http://axforum.info/forums/showthread.php?p=328140#post328140]Опасный orig[/url] commonOrig = _common.orig(); commonOrig.doClear(); ret = commonOrig.(_fieldId); } else { dictTable = new DictTable(_common.TableId); if (!(dictTable && dictTable.supportInheritance())) { // обычная табличка без наследования - все как обычно commonOrig = _common.orig(); ret = commonOrig.(_fieldId); } else { // обходим баг // [url]http://sashanazarov.blogspot.com/2014/01/dynamics-ax-2012-orig-method-fails-on.html[/url] // решаем проблемы с получением значений из родительских табличек для orig() буфера // ищем tableId в иерархии наследования для которой первой определено поле _fieldId т.е. ту для которой оно было введено, а не отнаследовано. fieldName = fieldId2name(_common.TableId, _fieldId); tableId4Field = _common.TableId; if(fieldName) { tableId = dictTable.extends(); while(tableId) { dictTable = new DictTable(tableId); if (fieldName2id(tableId, fieldName)) { tableId4Field = tableId; } else { break; } tableId = dictTable.extends(); } } if (tableId4Field) { commonCasted = SysDictTable::as(_common, tableId2name(tableId4Field)); commonOrig = commonCasted.orig(); ret = commonOrig.(_fieldId); } else { // не должны сюда попасть. Можно кидать исключение. commonOrig = _common.orig(); ret = commonOrig.(_fieldId); } } } return ret; } иллюстрация работы метода origFieldValue_MRC - джоб: X++: // [url]http://sashanazarov.blogspot.com/2014/01/dynamics-ax-2012-orig-method-fails-on.html[/url] static void reproOrigBug_MRC(Args _args) { CompanyInfo companyInfo; DirPartyTable dirPartyTable; Common common5; Common common6; anytype any; ; select firstOnly companyInfo; info(strFmt("Поле СompanyInfo.DataAreaId (системное. Определено на СompanyInfo). Значение \"%1\"", companyInfo.DataAreaId)); info(strFmt("Поле СompanyInfo.VATNum (не наследовано). Значение \"%1\"", companyInfo.VATNum)); info(strFmt("Поле СompanyInfo.Name (наследовано из DirPartyTable - от головной таблички в иерархии наследования). Значение \"%1\"", companyInfo.Name)); info(strFmt("Поле СompanyInfo.PhoneticName (наследовано из DirOrganizationBase - от промежуточной таблички в иерархии наследования. 1-й уровень наследования). Значение \"%1\"", companyInfo.PhoneticName)); info(strFmt("Поле СompanyInfo.DEL_RelationTypeName_OMInternalOrg (наследовано из OMInternalOrganization - от промежуточной таблички в иерархии наследования. 2-й уровень наследования). Значение \"%1\"", companyInfo.DEL_RelationTypeName_OMInternalOrg)); info(""); info("Теперь проверяем работу Orig()"); info(""); info("1. Обычный вызов companyInfo.orig().FieldName - для полей из таблиц родителей - теряем значения"); info(strFmt("companyInfo.orig().DataAreaId = \"%1\"", companyInfo.orig().DataAreaId)); info(strFmt("companyInfo.orig().VATNum = \"%1\"", companyInfo.orig().VATNum)); info(strFmt("companyInfo.orig().Name = \"%1\" %2", companyInfo.orig().Name, (companyInfo.orig().Name ? "" : "Потеряли значение !"))); info(strFmt("companyInfo.orig().PhoneticName = \"%1\" %2", companyInfo.orig().PhoneticName, (companyInfo.orig().PhoneticName ? "" : "Потеряли значение !"))); info(strFmt("companyInfo.orig().DEL_RelationTypeName_OMInternalOrg = \"%1\" %2", companyInfo.orig().DEL_RelationTypeName_OMInternalOrg, (companyInfo.orig().DEL_RelationTypeName_OMInternalOrg ? "" : "Потеряли значение !"))); info(""); dirPartyTable = companyInfo as DirPartyTable; info("2. Явно приводим тип к табличной переменной другого типа. dirPartyTable = companyInfo as DirPartyTable. Вызов dirPartyTable.orig().FieldName. Лечит проблему но неудобно использовать. Надо помнить из какой таблички пришло поле в иерархии наследования. Держать в коде отдельную табличную переменную другого типа."); info(strFmt("dirPartyTable.orig().DataAreaId = \"%1\"", dirPartyTable.orig().DataAreaId)); info(strFmt("dirPartyTable.orig().VATNum - НЕПРИМЕНИМО" /*, dirPartyTable.orig().VATNum*/)); info(strFmt("dirPartyTable.orig().Name = \"%1\"", dirPartyTable.orig().Name)); info(strFmt("dirPartyTable.orig().PhoneticName - НЕПРИМЕНИМО" /*, dirPartyTable.orig().PhoneticName*/)); info(strFmt("dirPartyTable.orig().DEL_RelationTypeName_OMInternalOrg - НЕПРИМЕНИМО" /*, dirPartyTable.orig().DEL_RelationTypeName_OMInternalOrg*/)); info(""); info("3. Попытка достать через common.orig() и прочие танцы с бубном - не помогло"); common5 = null; common6 = null; common5 = new SysDictTable(tableNum(DirPartyTable)).makeRecord(); // тип DirPartyTable common5.data(companyInfo); // тип DirPartyTable , но это не помогает вытащить правильное значение common6 = common5.orig(); // тип DirPartyTable , но это не помогает вытащить правильное значение info(strFmt("common6.(fieldNum(DirPartyTable, Name)) = \"%1\"", common6.(fieldNum(DirPartyTable, Name)))); common5 = null; common6 = null; common5 = new SysDictTable(tableNum(DirPartyTable)).makeRecord(); // тип DirPartyTable , но это не помогает вытащить правильное значение common5.data(companyInfo.orig()); // вот тут уже при вызове Orig() значение потеряно common6 = common5; info(strFmt("common6.(fieldNum(DirPartyTable, Name)) = \"%1\"", common6.(fieldNum(DirPartyTable, Name)))); info(""); info("4. Игры с приведением типов через переменную с типом anyType - все хорошо, но неудобно использовать."); any = new SysDictTable(tableNum(DirPartyTable)).makeRecord(); // any - тип DirPartyTable any = companyInfo; // any - все равно тип DirPartyTable - в этот момент происходит "приведение" типа. common5 = any; // common5 - тип DirPartyTable common6 = common5.orig(); // common6 - тип DirPartyTable info(strFmt("common6.(fieldNum(DirPartyTable, Name)) = \"%1\"", common6.(fieldNum(DirPartyTable, Name)))); info(""); info("5. Достаем значение вызовом origFieldValue_MRC(companyInfo, ...) - все нормально. Удобно использовать."); info(strFmt("origFieldValue_MRC(companyInfo, fieldnum(CompanyInfo, DataAreaId)) = \"%1\"", origFieldValue_MRC(companyInfo, fieldnum(CompanyInfo, DataAreaId)))); info(strFmt("origFieldValue_MRC(companyInfo, fieldnum(CompanyInfo, VATNum)) = \"%1\"", origFieldValue_MRC(companyInfo, fieldnum(CompanyInfo, VATNum)))); info(strFmt("origFieldValue_MRC(companyInfo, fieldnum(CompanyInfo, Name)) = \"%1\"", origFieldValue_MRC(companyInfo, fieldnum(CompanyInfo, Name)))); info(strFmt("origFieldValue_MRC(companyInfo, fieldnum(CompanyInfo, PhoneticName)) = \"%1\"", origFieldValue_MRC(companyInfo, fieldnum(CompanyInfo, PhoneticName)))); info(strFmt("origFieldValue_MRC(companyInfo, fieldnum(CompanyInfo, DEL_RelationTypeName_OMInternalOrg)) = \"%1\"", origFieldValue_MRC(companyInfo, fieldnum(CompanyInfo, DEL_RelationTypeName_OMInternalOrg)))); info(""); } Последний раз редактировалось Logger; 25.09.2018 в 11:38.  | 
| 
	
 | 
| 
			
			 | 
		#4 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Вот еще интересный джоб 
		
		
		
		
		
		
		
		
			X++: static void reproOrigBug2_MRC(Args _args) { CompanyInfo companyInfo; Common common5; Common common6; anytype anytypeVar; ; common5 = null; common6 = null; select firstOnly companyInfo; common5 = companyInfo as DirPartyTable; // здесь отладчик покажет что common5 - тип companyInfo anytypeVar = new SysDictTable(tableNum(DirPartyTable)).makeRecord(); // здесь отладчик покажет что anytypeVar - тип DirPartyTable anytypeVar = companyInfo; // и здесь anytypeVar - остался с типом DirPartyTable хотя присваивали companyInfo - ну не может проинициализированная переменная anyType сменить тип. common6 = anytypeVar; // тоже тип DirPartyTable } Забыл написать - проверял все на DAX 2012 R3 CU13 Последний раз редактировалось Logger; 25.09.2018 в 11:42.  | 
| 
	
 | 
| 
			
			 | 
		#5 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Вообще, все что связано с наследованием таблиц - какое то дырявое. 
		
		
		
		
		
		
		
	Вот еще до кучи http://sysdictcoder.com/inconsistent...ysDictCoder%29 setTmp и иерархические таблицы  | 
| 
	
 | 
| 
			
			 | 
		#6 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Я в свое время столкнулся с тем, что buffer.TableId для таблиц, использующих наследование, зачастую возвращает идентификатор не совсем той таблицы, к которой относится буфер. Вместо этого обычно возвращается идентификатор одной из родительских таблиц, явно фигурирующих в коде во время компиляции - см. также А что такого принципиального в 2012? По моему хороший разработчик без труда в ней разберется  
		
		
		
		
		
		
		
	   Нужную информацию содержит поле buffer.RelationType, но тут есть нюанс: в интерпретаторе Х++ обращение к этому полю безопасно даже для Common, а вот при генерации CIL это приводит к ошибке компиляции. Более безопасным оказалось вызывать buffer.getInstanceRelationType() - он возвращает название конкретной таблицы-наследника либо пустую строку, если таблица не входит в иерархию наследования. В итоге для определения во время выполнения "настоящего" типа табличного буфера с учетом возможного наследования получился такой вспомогательный метод:X++: // возвращает tableId таблицы либо, если таблица поддерживает наследование, то tableId конкретного "наследника" // safe тут относится к генерации кода CIL, которая валится на простом обращении к Common.RelationType public static TableId getTableIdOrReltaionTypeSafe(Common _record) { TableNameShort tableName = _record.getInstanceRelationType(); TableId ret = tableName2id(tableName); ; if (!ret) { ret = _record.TableId; } return ret; }  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: AlGol (3), raz (10), sukhanchik (8), Logger (8). | |
| 
			
			 | 
		#7 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
X++: // обходим баг вызова orig() для табличек с наследованием // [url=http://axforum.info/forums/showthread.php?p=412642#post412642]Приведение типов для таблиц ax2012[/url] // [url]http://sashanazarov.blogspot.com/2014/01/dynamics-ax-2012-orig-method-fails-on.html[/url] // [url=http://axforum.info/forums/showthread.php?p=328140#post328140]Опасный orig[/url] public static anyType origFieldValue_MRC(Common _common, FieldId _fieldId) { TableId tableId; TableId tableId4Field; FieldName fieldName; Common commonCasted; Common commonOrig; DictTable dictTable; anytype ret; ; if (_common.RecId == 0) { // обходим баг // [url=http://axforum.info/forums/showthread.php?p=328140#post328140]Опасный orig[/url] commonOrig = _common.orig(); commonOrig.doClear(); ret = commonOrig.(_fieldId); } else { dictTable = new DictTable(_common.TableId); if (!(dictTable && dictTable.supportInheritance())) { // обычная табличка без наследования - все как обычно commonOrig = _common.orig(); ret = commonOrig.(_fieldId); } else { // обходим баг // [url]http://sashanazarov.blogspot.com/2014/01/dynamics-ax-2012-orig-method-fails-on.html[/url] // решаем проблемы с получением значений из родительских табличек для orig() буфера dictTable = new DictTable(tableName2id(_common.getInstanceRelationType())); if (dictTable) { // ищем tableId в иерархии наследования для которой первой определено поле _fieldId т.е. ту для которой оно было введено, а не отнаследовано. fieldName = fieldId2name(_common.TableId, _fieldId); tableId4Field = _common.TableId; if(fieldName) { tableId = dictTable.extends(); while(tableId) { dictTable = new DictTable(tableId); if (fieldName2id(tableId, fieldName)) { tableId4Field = tableId; } else { break; } tableId = dictTable.extends(); } } } if (tableId4Field) { commonCasted = SysDictTable::as(_common, tableId2name(tableId4Field)); commonOrig = commonCasted.orig(); ret = commonOrig.(_fieldId); } else { // не должны сюда попасть. Можно кидать исключение. commonOrig = _common.orig(); ret = commonOrig.(_fieldId); } } } return ret; }  | 
| 
	
 | 
| 
			
			 | 
		#8 | 
| 
			
			 Молодой, подающий надежды 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
X++: SysDictTable::getConcreteTable(record); X++: /// <summary> /// Gets the ID for the concrete table for a specified table. /// </summary> /// <param name="_common"> /// The buffer of the given table whose concrete table must be found. /// </param> /// <returns> /// The table ID of the concrete table for the specified table. /// </returns> public static TableId getConcreteTable(Common _common) { DictTable dt=new DictTable(_common.TableId); TableId concrete=_common.TableId; str tablename; //Get concrete table for inheritance if(dt && dt.supportInheritance()) { tablename= _common.getInstanceRelationType(); if(tablename) { concrete=tableName2id(tablename); } } return concrete; } Последний раз редактировалось pedrozzz; 25.09.2018 в 18:12.  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: sukhanchik (4), Logger (1), gl00mie (3). | |
| 
			
			 | 
		#9 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Ха, а вот такой джоб 
		
		
		
		
		
		
		
		
			X++: static void reproOrigBug3_MRC(Args _args) { CompanyInfo companyInfo; DirPartyTable dirPartyTable; DirOrganizationBase dirOrganizationBase; Common common5; Common common5_orig; Common common6; Common common6_orig; anytype anytypeVar; ; common5 = null; common6 = null; select firstOnly companyInfo; info(strFmt("companyInfo.Name = %1", companyInfo.Name)); info(strFmt("companyInfo.orig().Name = %1", companyInfo.orig().Name)); dirOrganizationBase = companyInfo as DirOrganizationBase; info(strFmt("dirOrganizationBase.Name = %1", dirOrganizationBase.Name)); info(strFmt("dirOrganizationBase.orig().Name = %1", dirOrganizationBase.orig().Name)); dirPartyTable = companyInfo as DirPartyTable; info(strFmt("dirPartyTable.Name = %1", dirPartyTable.Name)); info(strFmt("dirPartyTable.orig().Name = %1", dirPartyTable.orig().Name)); common5 = companyInfo as DirPartyTable; // здесь отладчик покажет что common5 - тип companyInfo info(strFmt("common5.(fieldNum(dirPartyTable, Name)) = %1", common5.(fieldNum(dirPartyTable, Name)))); //info(strFmt("common5.orig().(fieldNum(dirPartyTable, Name)) = %1", common5.orig().(fieldNum(dirPartyTable, Name)))); // не компилируется common5_orig = common5.orig(); info(strFmt("common5_orig.(fieldNum(dirPartyTable, Name)) = %1", common5_orig.(fieldNum(dirPartyTable, Name)))); info(""); info("А так работает (с приведением типа)"); anytypeVar = new SysDictTable(tableNum(DirPartyTable)).makeRecord(); // здесь отладчик покажет что anytypeVar - тип DirPartyTable anytypeVar = companyInfo; // и здесь anytypeVar - остался с типом DirPartyTable хотя присваивали companyInfo - ну не может проинициализированная переменная anyType сменить тип. common6 = anytypeVar; // тоже тип DirPartyTable info(strFmt("common6.(fieldNum(dirPartyTable, Name)) = %1", common6.(fieldNum(dirPartyTable, Name)))); //info(strFmt("common6.orig().(fieldNum(dirPartyTable, Name)) = %1", common6.orig().(fieldNum(dirPartyTable, Name)))); // не компилируется common6_orig = common6.orig(); info(strFmt("common6_orig.(fieldNum(dirPartyTable, Name)) = %1", common6_orig.(fieldNum(dirPartyTable, Name)))); } Цитата: 
	
		
			companyInfo.Name = Company Dat 
companyInfo.orig().Name = dirOrganizationBase.Name = Company Dat dirOrganizationBase.orig().Name = icName dirPartyTable.Name = Company Dat dirPartyTable.orig().Name = Company Dat common5.(fieldNum(dirPartyTable, Name)) = Company Dat common5_orig.(fieldNum(dirPartyTable, Name)) = А так работает (с приведением типа) common6.(fieldNum(dirPartyTable, Name)) = Company Dat common6_orig.(fieldNum(dirPartyTable, Name)) = Company Dat ![]() в моем случае в поле companyInfo.name лежит значение "Company Dat" если начитанный буфер привести к типу dirOrganizationBase то dirOrganizationBase.orig().Name вернет вообще мусор ! Будет возвращено значение "icName" вместо "Company Dat" !!! Последний раз редактировалось Logger; 25.09.2018 в 16:27.  | 
| 
	
 | 
| 
			
			 | 
		#10 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Тут может быть проблема в том, что Common ведет себя иногда как ссылочный тип, иногда как значение.
		 
		
		
		
		
		
		
		
	 | 
| 
	
 | 
| 
			
			 | 
		#11 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
И получается в иерархии DirPartyTable ... CompanyInfo у нас есть набор интерфейсов по работе с ядреным курсором. При этом реализация DirPartyTable.orig() и CompanyInfo.orig() разная. Каждый знает только про свой набор полей - про тот набор, который мы в AOT для таблички описали. И именно этот набор полей при вызове и заполняет в возвращаемом буфере. Соответственно, задача стояла как получить доступ к соответствующему интерфейсу курсора. Если в коде объявлена переменная соответствующего типа, то все просто, а если ее нет, то как ее получить в runTime. Ну, я нашел способ. P.S. Кстати, аналогичная проблема возникает при помещении и извлечении буфера в List (см. http://sashanazarov.blogspot.com/201...n-objects.html) а также при помещении и извлечении в контейнер таким способом, популярным в предыдущих версиях: X++: con = [buffer]; buffer = conPeek(con, 1); Видимо, во всех этих случаях задействован какой-то общий механизм в ядре.  | 
| 
	
 | 
| 
			
			 | 
		#12 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Я хотел было побрюзжать, что стандартный метод всегда создает экземпляр DictTable, но вспомнил тут одну особенность ядра: я некогда его ковырял (интересно же) и обнаружил, что экземпляры Dict-классов не каждый раз создаются и удаляются по мере работы кода X++, а создаются один раз при загрузке, затем ссылки на их эксземпляры сохраняются в массиве указателей, где индексом является идентификатор соотв. объекта. Возможно, для объектов приложения используется ленивая инициализация Dict-классов, но для объектов ядра (классов и таблиц) инициализация массива указателей происходит при загрузке ядра. 
		
		
		
		
		
		
		
	Таким образом, new DictTable() или new DictClass() транслируется ядром в обращение к массиву указателей и в общем случае отрабатывает очень быстро, без выделения памяти и последующей работы сборщика мусора. Поэтому Dict-классы - это очень быстрый API отражения, в отличие, скажем, от TreeNode. Что примечательно, SysDict-классы такой особенностью, насколько я знаю, не обладают и создаются, как любые другие объекты Х++, так что с точки зрения производительности с SysDict-классами стоит быть осторожнее.  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: sukhanchik (4), pedrozzz (3), skuull (2). | |
| 
			
			 | 
		#13 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			То есть, если правильно понимаю, лучше не создавать SysDict*, а Dict* и потом приводить к SysDict*, если нужны методы именно Sys?
		 
		
		
		
		
		
		
		
	 | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: Logger (1). | |
| 
			
			 | 
		#14 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Вот откуда растут ноги у такого агрессивного кеширования всего на свете в 2012-й...
		 
		
		
		
		
		
		
		
	 | 
| 
	
 | 
| 
			
			 | 
		#15 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Ха! 
		
		
		
		
		
		
		
	Оказывается методы isSql() и Name(dbBackend::Sql) класса DictIndex возвращают неправильное значение для для табличек из иерархии наследования. isSql() - всегда false Name(dbBackend::Sql) - всегда '' Да что же это такое! Почему реализация работы с наследованием в ядре такая кривая ?  | 
| 
	
 | 
| 
			
			 | 
		#16 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Фикс с исправлением для SysDictIndex во вложении.
		 
		
		
		
			 | 
| 
	
 | 
| Теги | 
| inheritance, table inheritance, type cast | 
| 
	
	 | 
	
| Опции темы | Поиск в этой теме | 
| Опции просмотра | |
		
  |