Usando o SysExtension Framework para descobrir qual subclasse 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:11 UTC
Este artigo descreve como usar o pouco conhecido framework SysExtension no Dynamics AX 2012 e no Dynamics 365 for Operations para instanciar subclasses com base 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
As informações neste post são baseadas no Dynamics AX 2012 R3. Elas podem ou não ser válidas para outras versões. (Atualização: Posso confirmar que as informações neste artigo também são válidas para o Dynamics 365 for Operations.)
Ao implementar classes de processamento no Dynamics AX, você frequentemente se depara com a criação de uma hierarquia de classes na qual cada subclasse corresponde a um valor de enumeração ou possui algum outro tipo de acoplamento de dados. Uma abordagem clássica consiste em ter um método construtor na superclasse, que possui uma condição que determina qual classe instanciar com base na entrada.
Isso funciona bem em princípio, mas se você tiver muitas entradas possíveis (muitos elementos em uma enumeração ou talvez a entrada seja uma combinação de vários valores diferentes), a manutenção pode se tornar tediosa e propensa a erros, e o design sempre terá a desvantagem de que você precisará modificar o método construtor se adicionar uma nova subclasse ou fizer alterações em qual subclasse deve ser usada com base em qual entrada.
Felizmente, existe uma maneira muito mais elegante, mas infelizmente também muito menos conhecida, de fazer isso, ou seja, usando o framework SysExtension.
Essa estrutura aproveita atributos que você pode usar para decorar suas subclasses, permitindo que o sistema determine qual subclasse deve ser usada para lidar com cada situação. Você ainda precisará de um método construtor, mas, se implementado corretamente, nunca precisará modificá-lo ao adicionar novas subclasses.
Vamos considerar um exemplo hipotético e supor que você vai implementar uma hierarquia que realiza algum tipo de processamento com base na tabela InventTrans. O processamento a ser feito depende do StatusReceipt e do StatusIssue dos registros, bem como se os registros estão relacionados a SalesLine, PurchLine ou nenhum dos dois. Só agora você já está considerando diversas combinações possíveis.
Digamos então que você sabe que, por enquanto, só precisa lidar com algumas combinações, mas também sabe que, com o tempo, será solicitado que você lide com cada vez mais combinações.
Vamos manter as coisas relativamente simples e dizer que, por enquanto, você só precisa lidar com registros relacionados a SalesLine com um StatusIssue de ReservPhysical ou ReservOrdered. Todas as outras combinações podem ser ignoradas por enquanto, mas como você sabe que terá que lidar com elas mais tarde, é importante projetar seu código de forma que ele seja facilmente extensível.
Sua hierarquia pode se parecer com algo assim por enquanto:
- MeuProcessadorMeuProcessador_VendasMeuProcessador_Vendas_ReservadoPedidoMeuProcessador_Vendas_ReservadoFísico
Agora, você poderia facilmente implementar um método na superclasse que instancia uma subclasse baseada em um `ModuleInventPurchSales` e um enum `StatusIssue`. Mas, nesse caso, você precisaria modificar a superclasse sempre que adicionasse uma subclasse, e essa não é exatamente a ideia de herança na programação orientada a objetos. Afinal, você não precisa modificar `RunBaseBatch` ou `SysOperationServiceBase` toda vez que adiciona um novo job em lote.
Em vez disso, você pode usar o framework SysExtension. Isso exigirá que você adicione outra classe, que precisa estender SysAttribute. Essa classe será usada como o atributo que você poderá usar para decorar suas classes de processamento.
Essa classe é muito semelhante à forma como você criaria uma classe de contrato de dados para uma implementação de SysOperation, pois terá alguns membros de dados e métodos de parâmetros para obter e definir esses valores.
No nosso caso, a declaração da classe pode ter uma aparência semelhante a esta:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Você precisa criar um método `new()` para instanciar todos os membros de dados. Se desejar, pode atribuir valores padrão a alguns ou a todos eles, mas eu não fiz isso.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
Você também deve implementar um método `parm` para cada membro de dados, mas omiti isso aqui, pois tenho certeza de que você sabe como fazer - caso contrário, vamos considerar isso um exercício ;-)
Agora você pode usar sua classe de atributo para decorar cada uma de suas classes de processamento. Por exemplo, as declarações de classe poderiam 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
{
}
Você pode, naturalmente, nomear suas classes como quiser; o importante é decorá-las com atributos que correspondam ao tipo de processamento que realizam. (Lembre-se, porém, de que existem convenções de nomenclatura para hierarquias de classes no Dynamics AX, e é sempre recomendável segui-las, se possível).
Agora que você decorou suas classes para identificar o tipo de processamento que cada uma delas realiza, você pode aproveitar o framework SysExtension para instanciar objetos das subclasses conforme necessário.
Na sua superclasse (MyProcessor), você pode adicionar um método construtor 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;
}
Parte realmente interessante — e, na verdade, o objetivo (sem trocadilho) de toda esta postagem — é o método getClassFromSysAttribute() na classe SysExtensionAppClassFactory. O que esse método faz é aceitar o nome da superclasse de uma hierarquia (e essa superclasse não precisa estar no topo da hierarquia; isso simplesmente significa que apenas as classes que estendem essa classe serão elegíveis) e um objeto de atributo.
Em seguida, retorna um objeto de uma classe que estende a superclasse especificada e é decorado com um atributo correspondente.
Obviamente, você pode adicionar quantas validações ou lógica adicionais desejar ao método `construct`, mas o importante aqui é que, uma vez implementado, você nunca mais precisará modificar esse método. Você pode adicionar subclasses à hierarquia e, contanto que as decore adequadamente, o método `construct` as encontrará, mesmo que elas não existissem quando ele foi escrito.
Quanto ao desempenho? Sinceramente, não tentei fazer testes de benchmark, mas minha intuição me diz que provavelmente terá um desempenho pior do que o design clássico com a instrução switch. No entanto, considerando que a grande maioria dos problemas de desempenho no Dynamics AX são causados pelo acesso ao banco de dados, eu não me preocuparia muito com isso.
É claro que, se você estiver implementando algo que exija a criação rápida de milhares de objetos, talvez queira investigar mais a fundo, mas nos casos clássicos em que você apenas instancia um único objeto para realizar algum processamento demorado, duvido que faça diferença. Além disso, considerando minha dica de solução de problemas (próximo parágrafo), parece que o framework SysExtension depende de cache, então, em um sistema em execução, duvido que haja um impacto significativo no desempenho.
Solução de problemas: Se o método construtor não encontrar suas subclasses, mesmo que você tenha certeza de que elas estão decoradas corretamente, pode ser um problema de cache. Tente limpar o cache tanto no cliente quanto no servidor. Não deve ser necessário reiniciar o AOS, mas pode ser o último recurso.
Leitura adicional
Se você gostou deste post, você também pode gostar destas sugestões:
- A diferença entre data() e buf2Buf() no Dynamics AX 2012
- Erro "Nenhuma classe de metadados definida para objeto de contrato de dados" no Dynamics AX 2012
- Chamando serviços de documentos AIF diretamente do X++ no Dynamics AX 2012
