Třída Box

Stále vídám X++ programátory, kteří volají třídu Box v kódu, který se používá v databázových transakcích. To může způsobit ošklivé problémy – jak ukážu v tomto příspěvku.

Představte si takovýto kód, použitý na nějaké tabulce:

void delete()
{
    if (Box::yesNo("Are you sure?", DialogButton::No) == DialogButton::Yes)
    {
        super();
    }
}

Při mazání záznamu se zpracování zastaví a uživateli se zobrazí následující modální okno:

Toto chování má několik problémů – například co se má stát, když není žádný uživatel k dispozici, protože je mazání spuštěno z dávkové úlohy? (V případě serverových dávek dojde k chybě, protože daná metoda je vázaná na klienta, klietnské úlohy zkrátka zamrznou.)

Ale já chci ukázat ještě jiný problém, který nemusíte odhalit tak snadno. Otestujme onu metodu delete() následujícím kódem:

static void deleteMyTable(Args _args)
{
    MyTable mt;
    InventSum inventSum;
 
    ttsbegin;
 
    mt = MyTable::find("A1", true);
 
    inventSum = InventSum::find(mt.ItemId, mt.InventDimId, true);
 
    mt.delete();
 
    ttscommit;
}

Metoda delete() tabulky MyTable vypadá stejně jako výše (mohli byste použít tabulku PurchLine nebo jakoukoli jinou obsahujícího ItemId a InventDimId). Krom smazání záznamu v MyTable se také získává záznam z tabulky InventSum.

Když kód spustíte, vše funguje dle očekávání a zobrazí se vám modální okno. Teď nastartujte ještě jednoho klienta Dynamics AX a spusťte ten samý job ještě jednou, čímž nasimuluje dalšího uživatele. Kód se zastaví na řádku, který se dotazuje na InventSum, a zůstane stát dokud nekliknete na Yes či No v původním klientu AX. To může ve skutečnosti trvat méně než vteřinu, ale také hodiny (třeba pokud uživatel někam odešel) nebo se to nemusí stát nikdy.

Tento problém je způsoben tím, že tabulka InventSum (a několik dalších) používá pesimistické zamykání a my požadujeme zámek pro zápis, který je ale již držen transakcí v zablokovaném klientu. Tudíž všichni ostatní klienti musí čekat, dokud není záznam k dispozici. Jinými slovy, ostatní klienti zamrznou, pokud volají jakýkoli kód vyžadující takový zámek.

Jediné volání třídy Box na špatném místě může zablokovat všechny uživatele – a už jsem to zažil. Nejrozumnější je se třídě Box úplně vyhnout, nebo ji alespoň používat pouze z formulářů (kde by rozhodně neměli být žádné transakce). Pokud ji voláte v obecně využitelné byznys logice, nikdy nevíte, zda ji někdo nepoužívá v transakci.

A pokud opravdu musíte ignorovat všechna doporučení, podívejte se na application.ttsLevel() a použijte třídu Box pouze je-li transakční level roven nule.