Важное ПРИМЕЧАНИЕ: в примере ниже вы увидите создание объекта для экспорта данных из AX, вы не сможете импортировать данные с таким типом объекта.

Для некоторых сценариев вам может потребоваться создать DataEntity, у которого нет естественного ключа (например, InventTrans), или, что еще хуже, таблицы, у которых вообще нет уникального индекса (например, CustTrans). Кроме того, для таких типов сущностей у вас может быть требование — поддержка инкрементального push (что также является проблемой, если ваша таблица для DataEntity не имеет естественного ключа);

Чтобы удовлетворить эти требования, вам необходимо:

2.Создайте объект View (CustTransV) и в свойстве Query — вы должны указать объект CustTransQ, созданный на шаг раньше;

3. Обычно, используя мастер для создания DataEntity, вам нужно выбрать таблицу с естественным ключом, но для этого сценария вы можете выбрать ЛЮБУЮ таблицу с естественным ключом (это только с целью создания объекта DataEntity в AOT — назовем его MyCustTransEntity). Например — вы можете выбрать таблицу InventDim.
4.Убедитесь, что на этом шаге вы сняли все флажки (это означает, что наш вновь созданный DataEntity не будет содержать поля из таблицы InventDim).

5.Удалить таблицу InventDim из источника данных MyCustTransEntity;
6. Drug CustTransV в источник данных MyCustTransEntity;

7. Выберите те поля из CustTransV, которые необходимо экспортировать через DataEntity и лекарство, и перетащите их в узел Fields MyCustTransEntity. Поле CustTransV.RecId также необходимо разместить в узле Fields MyCustTransEntity;
8. Когда вы добавляете поле CustTransView.RecId в узел Fields, вам нужно переименовать его: скажем, из RecId1 во что-то вроде CustTransRecId;
9. Добавьте поле CustTransRecId в узел Entity Index;

После этих операций вам необходимо построить модель с синхронизацией. Итак, на данный момент ваш DataEntity может быть пользователем для экспорта данных. Чтобы использовать этот DataEntity для инкрементного экспорта данных, вам необходимо создать класс, который будет обрабатывать некоторые события класса DMFEntityBase (класс DMFEntityBase отвечает за анализ DataEntities). Итак, ниже вы увидите класс, который позволяет:
1. Используйте представления в качестве источника данных для вашего DataEntity
2. Проверьте, включено ли отслеживание изменений (CT) для вашего DataEntity-> View-> Query-Table
a.Таким образом, он включит / отключит CT, если вы добавите / удалите свой DataEntityl;

///
/// Fixing of MS wrong logic if DataSource for DataEntity is View (sourced by Query)
///
using DataManagementSerialization = Microsoft.Dynamics.AX.Framework.Tools.DataManagement.Serialization;
using Microsoft.Dynamics.BusinessPlatform.SharedTypes;
using Microsoft.Dynamics.ApplicationFoundation.DIXF.Instrumentation;

class DMFEntityBase_YourModel_ClassEH
{
const static str qtyOfTablesCTEnabledArgName = ‘qtyOfTablesCTEnabled’;
const static str isCTEnabledArgName = ‘isCTEnabled’;

///

///
///

/// ///
/// Fixing of MS wrong logic if DataSource for DataEntity is View (sourced by Query)
///[PreHandlerFor(classStr(DMFEntityBase), staticMethodStr(DMFEntityBase, DisableCTForEntity))] public static void DMFEntityBase_Pre_DisableCTForEntity(XppPrePostArgs _args)
{
DMFEntity entityloc = _args.getArg(‘entityloc’);
boolean isCTEnabledForTable = false;
int qtyOfTablesCTEnabled = 0;
DictDataEntity dictDataEntity ;
AifSqlChangeTrackingEnabledTables aifSqlChangeTrackingEnabledTables;

dictDataEntity = new DictDataEntity(tableName2Id(entityloc.TargetEntity));
TableName rootTableName = DMFEntityBase::getRootTableName(dictDataEntity);

if(rootTableName && entityloc.TargetEntity != «»)
{
isCTEnabledForTable = AifSqlChangeTrackingEnabledTables::find(rootTableName, entityloc.TargetEntity).RecId != 0;

if (isCTEnabledForTable)
{
select count(RecId) from aifSqlChangeTrackingEnabledTables
where aifSqlChangeTrackingEnabledTables.TableName == rootTableName;

qtyOfTablesCTEnabled = aifSqlChangeTrackingEnabledTables.RecId;
}

}

_args.addArg(DMFEntityBase_YourModel_ClassEH::qtyOfTablesCTEnabledArgName, aifSqlChangeTrackingEnabledTables.RecId);
_args.addArg(DMFEntityBase_YourModel_ClassEH::isCTEnabledArgName, isCTEnabledForTable);
}

///

///
///

/// ///
/// Fixing of MS wrong logic if DataSource for DataEntity is View (sourced by Query)
///[PostHandlerFor(classStr(DMFEntityBase), staticMethodStr(DMFEntityBase, DisableCTForEntity))] public static void DMFEntityBase_Post_DisableCTForEntity(XppPrePostArgs _args)
{
DMFEntity entityloc = _args.getArg(‘entityloc’);
boolean isCTEnabledForTable = _args.getArg(DMFEntityBase_YourModel_ClassEH::isCTEnabledArgName);
int qtyOfTablesCTEnabled = _args.getArg(DMFEntityBase_YourModel_ClassEH::qtyOfTablesCTEnabledArgName);
DictDataEntity dictDataEntity ;
AifSqlChangeTrackingEnabledTables aifSqlChangeTrackingEnabledTables;
TableName rootTableName;
boolean isRecFromAifCTTbleDeleted = false;

AifChangeTrackingQuery query;
AifChangeTrackingDataSource dataSource;
int dataSourceIndex, dataSourceCount;
TableName tableName;
AifSqlChangeTrackingEnabledTables enabledTablesDelete;
AIFSQLCDCENABLEDTABLES ctEnabledTables;
boolean isEntityEnabled, rootCTDisabled;
boolean trigerCTDisabled = false;
AifChangeTrackingScope scope;
int64 ctEnabled = -1;

if (isCTEnabledForTable && qtyOfTablesCTEnabled > 0)
{
dictDataEntity = new DictDataEntity(tableName2Id(entityloc.TargetEntity));
rootTableName = DMFEntityBase::getRootTableName(dictDataEntity);

select count(RecId) from aifSqlChangeTrackingEnabledTables
where aifSqlChangeTrackingEnabledTables.TableName == rootTableName;

isRecFromAifCTTbleDeleted = qtyOfTablesCTEnabled != aifSqlChangeTrackingEnabledTables.RecId
&& qtyOfTablesCTEnabled > aifSqlChangeTrackingEnabledTables.RecId;

}

if (!isRecFromAifCTTbleDeleted && isCTEnabledForTable && qtyOfTablesCTEnabled > 0)
{
scope = entityloc.TargetEntity +»@DMF:DMFExport»;

select count(RecId) from ctEnabledTables where ctEnabledTables.Scope == scope;
isEntityEnabled = ctEnabledTables.RecId > 1 ? true:false;

if(dictDataEntity && AifSqlChangeTrackingEnabledTables::find(rootTableName, entityloc.TargetEntity).RecId)
{
query = AifChangeTrackingQuery::constructFromDataEntity(dictDataEntity,isEntityEnabled);

ttsbegin;
dataSourceCount = query.dataSourceCount();

for (dataSourceIndex = 1; dataSourceIndex <= dataSourceCount; dataSourceIndex++)
{
dataSource = query.dataSourceNo(dataSourceIndex);
tableName = tableId2name(dataSource.table());

new AifChangeTrackingPermission().assert();

ctEnabled = DMFEntityBase_YourModel_ClassEH::checkCT(tableName);
if (ctEnabled == 1 && qtyOfTablesCTEnabled == 1)
{
DMFEntityBase::DisableCT(tableName);
}

AifChangeTrackingConfiguration::disableChangeTracking(scope);
if (tableName == rootTableName)
{
rootCTDisabled = true;
}
CodeAccessPermission::revertAssert();

if (rootCTDisabled)
{
//If root table CT is disabled, delete record without disbaling CT for it
enabledTablesDelete = AifSqlChangeTrackingEnabledTables::find(tableName, entityloc.TargetEntity, true);

if(enabledTablesDelete && enabledTablesDelete.validateDelete())
{
enabledTablesDelete.delete();
}
}
}
ttscommit;
info(strFmt(«@DMF:DMFCTDisableSuccess», entityloc.EntityName));
}
}
}

///

///
///

/// ///
/// Fixing of MS wrong logic if DataSource for DataEntity is View (sourced by Query)
///[PostHandlerFor(classStr(DMFEntityBase), staticMethodStr(DMFEntityBase, getRootTableName))] public static void DMFEntityBase_Post_getRootTableName(XppPrePostArgs _args)
{
#AOT
#Properties
#TreeNodeSysNodeType

SysDictTable dictTable;
TreeNode viewsNode;
TreeNode viewNode;
str viewQueryName;
Query viewQuery;
QueryBuildDataSource viewRootDataSource;

AifSqlChangeTrackingEnabledTables aifSqlChangeTrackingEnabledTables;
TableName rootTableName = _args.getReturnValue();
Query query;
QueryBuildDataSource qbds;
DictDataEntity dictDataEntity = _args.getArg(‘dictDataEntity’);
DMFEntityName entityName;
int dataSourceIndex;
int dataSourceCount;
boolean isCTEnabledForTable = false;

if (dictDataEntity != null)
{
entityName = dictDataEntity.name();

isCTEnabledForTable = AifSqlChangeTrackingEnabledTables::find(rootTableName, entityName).RecId != 0;

if (!isCTEnabledForTable)
{

// Code access security to ensure API is only called on the server
new AifChangeTrackingPermission().demand();

query = dictDataEntity.query();
qbds = query.dataSourceNo(1);

dictTable = new SysDictTable(qbds.table());

if (dictTable.isView())
{
// Get view node
viewsNode = TreeNode::findNode(#ViewsPath);
viewNode = viewsNode.AOTfindChild(dictTable.name());

// Get view query
if (viewNode != null)
{
viewQueryName = viewNode.AOTgetProperty(#PropertyQuery);
if (viewQueryName != «»)
{
viewQuery = new Query(viewQueryName);
if (viewQuery != null)
{
viewRootDataSource = viewQuery.dataSourceNo(1);
rootTableName = tableId2Name(viewRootDataSource.table());
}
}
isCTEnabledForTable = AifSqlChangeTrackingEnabledTables::find(rootTableName, entityName).RecId != 0;
if (isCTEnabledForTable)
{
_args.setReturnValue(rootTableName);
}
}
}
}
}
}

///

/// Disbles change tracking for the provided table.
///

/// Table name /// true or false
///
/// Fixing of MS wrong logic if DataSource for DataEntity is View (sourced by Query)
///
public static int64 checkCT(TableName tableName)
{
InteropPermission permission;
str errorString;
CLRObject CLRexception;

System.Data.SqlClient.SqlDataReader sqlDataReader;
str sql,connectionStr, physicalTableName;
System.Data.SqlClient.SqlConnection sqlConn;
System.Data.SqlClient.SqlCommand sqlComm;
SysDictTable dictTable;
int64 rv = -1;
AifSqlCtChangeTracking act = new AifSqlCtChangeTracking();

permission = new InteropPermission(InteropKind::ClrInterop);
permission.assert();

try
{
dictTable = new SysDictTable(tableName2id(tableName));
if (dictTable && !dictTable.isView())
{
physicalTableName = AifChangeTracking::getHierarchyRootTablePhysicalName(dictTable.id());

if(act.isChangeTrackingEnabledForTable(tableName2id(physicalTableName)))
{
connectionStr = SqlSystem::managedConnectionString();

sql = strfmt(‘SELECT COUNT(*) FROM sys.change_tracking_tables WHERE object_id = OBJECT_ID(‘%1′)’, physicalTableName);

sqlConn = new System.Data.SqlClient.SqlConnection(connectionStr);

sqlConn.Open();
sqlComm = sqlConn.CreateCommand();
sqlComm.set_CommandText(sql);
sqlDataReader = sqlComm.ExecuteReader();

if (sqlDataReader && sqlDataReader.Read())
{
rv = sqlDataReader.GetInt32(0);
}
sqlComm.Dispose();
sqlDataReader.Dispose();
sqlConn.Close();
}
}
return rv;
}
catch (Exception::Error)
{
error(strFmt(«@DMF:DMFDisableCTError», physicalTableName));
return false;
}

catch( Exception::CLRError )
{
//Cannot inititalize connection to the source system
error(«@SYS134042»);

//BP Deviation documented
CLRexception = CLRInterop::getLastException();
while( CLRexception )
{
//BP Deviation documented
errorString += CLRInterop::getAnyTypeForObject( CLRexception.get_Message() );

CLRexception = CLRexception.get_InnerException();
}

throw error(errorString);
}
CodeAccessPermission::revertAssert();
}

///

///
///

/// ///
/// Fixing of MS wrong logic if DataSource for DataEntity is View (sourced by Query)
///[PreHandlerFor(classStr(DMFEntityBase), staticMethodStr(DMFEntityBase, DisableCT))] public static void DMFEntityBase_Pre_DisableCT(XppPrePostArgs _args)
{
const str ctIsNotEnabledForThisTable = ‘NotExistTable’;
const str tableNameArgName = ‘tableName’;
TableName tableName = _args.getArg(‘tableName’);
int64 rv = -1;

rv = DMFEntityBase_YourModel_ClassEH::checkCT(tableName);
if (rv <= 0) { _args.setArg(tableNameArgName, ctIsNotEnabledForThisTable); } } /// ///Fixing of MS wrong logic if DataSource for DataEntity is View (sourced by Query) /// public static boolean isCTEnabledForAllTablesInView(DMFEntity _entity) { TableName rootTableName; AifChangeTrackingQuery query; AifChangeTrackingDataSource dataSource; int dataSourceIndex, dataSourceCount; TableName tableName; AIFSQLCDCENABLEDTABLES ctEnabledTables; boolean isEntityEnabled; boolean ret = true; AifChangeTrackingScope scope; int64 ctEnabled; Query q; QueryBuildDataSource qbds; SysDictTable dictTable; DictDataEntity dictDataEntity; dictDataEntity = new DictDataEntity(tableName2Id(_entity.TargetEntity)); q = dictDataEntity.query(); qbds = q.dataSourceNo(1); dictTable = new SysDictTable(qbds.table()); if (dictTable && !dictTable.isView()) { return checkFailed(strFmt(«For DataEntity %1, for DataSource, not a View is used», dictDataEntity.name())); } if (_entity == null) { return checkFailed(strFmt(«DataEntity %1 is not exists», dictDataEntity.name())); } rootTableName = DMFEntityBase::getRootTableName(dictDataEntity); scope = _entity.TargetEntity +»@DMF:DMFExport»; select count(RecId) from ctEnabledTables where ctEnabledTables.Scope == scope; isEntityEnabled = ctEnabledTables.RecId >= 1 ? true:false;

if(isEntityEnabled && AifSqlChangeTrackingEnabledTables::find(rootTableName, _entity.TargetEntity).RecId)
{
query = AifChangeTrackingQuery::constructFromDataEntity(dictDataEntity,isEntityEnabled);

dataSourceCount = query.dataSourceCount();

for (dataSourceIndex = 1; dataSourceIndex <= dataSourceCount; dataSourceIndex++)
{
dataSource = query.dataSourceNo(dataSourceIndex);
tableName = tableId2name(dataSource.table());

new AifChangeTrackingPermission().assert();
ctEnabled = -1;
ctEnabled = DMFEntityBase_YourModel_ClassEH::checkCT(tableName);
if (ctEnabled <= 0)
{
info(strFmt(«For table %1, in View %2, in DataEnitity %3 change tracking is not enabled (in sys.change_tracking_tables table)»,
tableName,
dictTable.name(),
dictDataEntity.name()));
ret = false;

}
CodeAccessPermission::revertAssert();
}
}
return ret;
}

}

Published On: 31 октября, 2018 / Рубрики: Blog /

Следите за новостями компании OntargIT

    Add notice about your Privacy Policy here.