Utilização do SysExtension Framework para descobrir qual a subclasse a instanciar no Dynamics AX 2012
Publicado: 16 de fevereiro de 2025 às 00:25:55 UTC
Última atualização: 12 de janeiro de 2026 às 08:43:12 UTC
Este artigo descreve como usar o pouco conhecido framework SysExtension em Dynamics AX 2012 e Dynamics 365 for Operations para instanciar subclasses baseadas em decorações de atributos, permitindo um design facilmente extensível de uma hierarquia de classes de processamento.
Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012
A informação neste artigo baseia-se no Dynamics AX 2012 R3. Pode ou não ser válida para outras versões. (Atualização: Posso confirmar que a informação deste artigo também é válida para o Dynamics 365 for Operations)
Ao implementar classes de processamento no Dynamics AX, muitas vezes depara-se com a criação de uma hierarquia de classes em que cada subclasse corresponde a um valor de enum ou tem algum outro acoplamento de dados. Um design clássico é então ter um método construct na superclasse, que tem um interruptor que determina qual classe instanciar com base na entrada.
Isto funciona bem em princípio, mas se tiveres muitas entradas possíveis diferentes (muitos elementos num enum ou talvez a entrada seja uma combinação de vários valores diferentes), pode tornar-se cansativo e propenso a erros de manter e o design tem sempre a desvantagem de teres de modificar esse método de construção se alguma vez adicionares uma nova subclasse ou fizeres alterações a qual subclasse deve ser usada com base em que entrada.
Felizmente, existe uma forma muito mais elegante, mas infelizmente também muito menos conhecida, de o fazer, nomeadamente através do uso do framework SysExtension.
Esta estrutura aproveita atributos que podes usar para decorar as tuas subclasses para permitir que o sistema perceba que subclasse deve ser usada para lidar com o quê. Ainda vais precisar de um método de construto, mas se for feito corretamente, nunca terás de o modificar ao adicionar novas subclasses.
Vamos olhar para um exemplo imaginário e supor que vais implementar uma hierarquia que faz algum tipo de processamento baseado na tabela InventTrans. Qual o processamento a realizar depende do StatusReceipt e do StatusIssue dos registos, bem como de se os registos estão relacionados com a SalesLine, PurchLine ou nenhum dos dois. Já agora, estás a olhar para muitas combinações diferentes.
Digamos então que sabes que, por agora, só precisas de lidar com algumas combinações, mas também sabes que te vão pedir que consigas lidar com mais e mais combinações ao longo do tempo.
Vamos manter as coisas relativamente simples e supor que, por agora, só precisa de tratar de registos relacionados com o SalesLine com um StatusIssue de ReservPhysical ou ReservOrdered, todas as outras combinações podem ser ignoradas por agora, mas como sabe que terá de as tratar mais tarde, vai querer desenhar o seu código de forma a que seja facilmente extensível.
A tua hierarquia pode parecer algo assim por agora:
- MyProcessorMyProcessor_SalesMyProcessor_Sales_ReservOrderedMyProcessor_Sales_ReservPhysical
Agora, poderia facilmente implementar um método na superclasse que instância uma subclasse baseada num ModuleInventPurchSales e num enum StatusIssue. Mas depois terá de modificar a superclasse sempre que adicionar uma subclasse, e essa não é realmente a ideia de herança na programação orientada a objetos. Afinal, não precisas de modificar o RunBaseBatch ou o SysOperationServiceBase sempre que adicionas um novo batch job.
Em vez disso, pode utilizar o framework SysExtension. Isso vai exigir que adiciones outra classe, que precisa de estender o SysAttribute. Esta classe será usada como o atributo com que podes decorar as tuas aulas de processamento.
Esta classe é muito semelhante à forma como se faz uma classe de contrato de dados para uma implementação do SysOperation, no sentido em que terá alguns membros de dados e métodos parm para obter e definir esses valores.
No nosso caso, a ClassDeclaration pode parecer-se mais ou menos assim:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Precisa de criar um novo método para instanciar todos os membros de dados. Se quiseres, podes dar a alguns ou a todos os valores padrão, mas eu nunca fiz isso.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
E também deves implementar um método parm para cada membro dos dados, mas omiti esses aqui porque tenho a certeza que sabes como fazer isso – caso contrário, vamos considerar isto um exercício ;-)
Agora podes usar a tua classe de atributos para decorar cada uma das tuas classes de processamento. Por exemplo, as declarações de classe podem ser assim:
StatusIssue::None,
StatusReceipt::None)]
class MyProcessor_Sales extends MyProcessor
{
}
[MyProcessorSystemAttribute(ModuleInventPurchSales::Sales,
StatusIssue::ReservOrdered,
StatusReceipt::None)]
class MyProcessor_Sales_ReservOrdered extends MyProcessor_Sales
{
}
[MyProcessorSystemAttribute(ModuleInventPurchSales::Sales,
StatusIssue::ReservPhysical,
StatusReceipt::None)]
class MyProcessor_Sales_ReservPhysical extends MyProcessor_Sales
{
}
Claro que podes nomear as tuas classes como quiseres, a parte importante aqui é que decoras as tuas classes com atributos que correspondam ao tipo de processamento que fazem. (Mas lembra-te que existem convenções de nomenclatura para hierarquias de classes no Dynamics AX e é sempre boa ideia segui-las, se possível).
Agora que decoraste as tuas classes para identificar que tipo de processamento cada uma faz, podes aproveitar o framework SysExtension para instanciar objetos das subclasses conforme necessário.
Na sua superclasse (MyProcessor), poderia adicionar um método de construção como este:
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
MyProcessor ret;
MyProcessorSystemAttribute attribute;
;
attribute = new MyProcessorSystemAttribute( _module,
_statusIssue,
_statusReceipt);
ret = SysExtensionAppClassFactory::getClassFromSysAttribute(classStr(MyProcessor), attribute);
if (!ret)
{
// no class found
// here you could throw an error, instantiate a default
// processor instead, or just do nothing, up to you
}
return ret;
}
A parte realmente interessante – e na verdade o objeto (desculpem o trocadilho) de todo este texto – é o método getClassFromSysAttribute() na classe SysExtensionAppClassFactory. O que este método faz é aceitar o nome da superclasse de uma hierarquia (e esta superclasse não precisa de estar no topo da hierarquia; significa simplesmente que apenas classes que estendem esta classe serão elegíveis) e um objeto atributo.
Depois, devolve um objeto de uma classe que estende a superclasse especificada e é decorado com um atributo correspondente.
Obviamente, podes adicionar tanta validação ou lógica adicional ao método construct quanto quiseres, mas o importante aqui é que, uma vez implementado, nunca mais deves ter de modificar este método. Podes adicionar subclasses à hierarquia e, desde que as decores adequadamente, o método do construto irá encontrá-las, mesmo que não existissem quando foi escrito.
E quanto ao desempenho? Honestamente, ainda não tentei fazer benchmarks, mas o meu instinto é que provavelmente tem um desempenho pior do que o design clássico de declaração de interruptor. No entanto, considerando que, de longe, a maioria dos problemas de desempenho no Dynamics AX é causada pelo acesso à base de dados, não me preocuparia demasiado com isso.
Claro que, se estás a implementar algo que vai exigir milhares de objetos para serem criados rapidamente, podes querer investigar mais, mas nos casos clássicos em que apenas instancias um único objeto para fazer um processamento longo, duvido que isso faça diferença. Além disso, tendo em conta a minha dica de resolução de problemas (próximo parágrafo), parece que o framework SysExtension depende de cache, por isso num sistema em execução duvido que tenha uma perda significativa de desempenho.
Resolução de Problemas: Se o método construct não encontrar as suas subclasses, mesmo tendo a certeza de que estão decoradas corretamente, pode ser um problema de cache. Tenta limpar caches tanto no cliente como no servidor. Não deve ser necessário reiniciar o AOS, mas pode ser o último recurso.
Leitura adicional
Se gostou deste post, também pode gostar destas sugestões:
- Eliminar uma entidade legal (contas da empresa) no Dynamics AX 2012
- Como iterar sobre os elementos de um Enum a partir do código X++ no Dynamics AX 2012
- Formatação de String com Macro e strFmt no Dynamics AX 2012
