úterý 28. července 2009

SysCompareCreateProject

Dnes jsem potřeboval vypsat objekty existující v jedné vrstvě a změněné v jiné vrstvě. Nejdřív mě napadl upgrade projekt, ale pak jsem objevil třídu SysCompareCreateProject. Zadám vrstvy, jméno privátního projektu a jedem… Jenže do projektu se mi dostaly i ty objekty, které ve vyšší vrstvě prostě chyběly, a to jsem nechtěl. Pokusně jsem upravil podmínku v compareNodes() a vše bylo OK. Před zásahem do dialogu (aby bylo možné chování měnit) jsem se ještě podíval do AX2009 (tohle bylo na 3.0) a co myslíte? Jasně, přesně tohle tam už je implementováno. :-)

neděle 19. července 2009

Reflexe - vytvoření objektu, volání metod

Původně jsem se chtěl vrátit k Team Foundation Serveru, ale času se nějak nedostává a plány se odsouvají... Takže raději zpátky k tématu reflexe. V poslední době jsem níže uvedené informace vyvsvětloval postupně několika lidem, tak bude asi vhodné to sepsat. Podívejme se na případ, kdy máme jméno (nebo ID) třídy a chceme vytvořit instanci.
ClassName className = 'SalesFormLetter_Invoice';
SysDictClass dictClass = new SysDictClass(className2Id(className));
FormLetter formLetter;
;
formLetter = dictClass.makeObject(true);
Jméno jsem převedl na ID, vytvořil instanci SysDictClass a zavolal metodu makeObject(). Té lze předat parametry, které očekává konstruktor. Je zajímavé, že nedojde k run-time chybě ani když vynechám povinný parametr (AX přiřadí parametru default hodnotu). Vtipně provádí validaci SysDictClass.allowMakeObject(). :-) Teď můžeme zavolat metodu objektu FormLetter. Na tom nic není. Ale co když neznám typ objektu? Mohu například zjistit, zda třída implementuje nějaké rozhraní a přetypovat na něj.
if (dictClass.isImplementing(classNum(SysRunable)))
{
   sysRunable = dictClass.makeObject();
   sysRunable.run();
}
Obdobně zjistím, zda je potomkem nějaké třídy.
if (dictClass.isExtending(classNum(RunBaseBatch)))
Nebo prostě zkusím, zda má objekt nějakou konkrétní metodu:
IdentifierName methodName = 'xml';
SysDictClass dictClass = new SysDictClass(classNum(Query));
Object object;
;

object = dictClass.makeObject();

if (dictClass.hasObjectMethod(methodName))
{
   info(dictClass.callObject(methodName, object, 10));
}
Metoda callObject() tedy očekává název metody, instanci objektu, jehož metoda se má volat, a případné parametry metody. Obdobně lze volat i statické metody. Jedno z možných použití je samozřejmě i vytvoření instance:
SysDictClass dictClass = new SysDictClass(classNum(InventItemType));
Object object;
;
object = dictClass.callStatic('construct', ItemType::Service);
U tabulek je to pak velmi podobné, jen místo makeObject() se použije makeRecord():
SysDictTable dictTable = new SysDictTable(tableNum(LedgerJournalTrans));
Common common;
;
common = dictTable.makeRecord();
dictTable.callObject('initFromCustTable', common, CustTable::find('1'));
U tabulek nás ale většinou zajímá jestě jedna věc a tou věcí jsou datová pole. Tam přijde ke slovu trochu neobvyklá syntaxe tabulka.(číslo pole). Například:
InventTable inventTable = InventTable::find('1');
SalesLine   salesLine = SalesLine::findInventTransId('12345', true);
Common      table1 = inventTable;
Common      table2 = salesLine;
FieldId     fieldId1 = fieldNum(InventTable, ItemId);
FieldId     fieldId2 = fieldNum(SalesLine, ItemId);
;
table1.(fieldId1) = table2.(fieldId2);
Poslední příklad ukazuje, jak získat hodnotu na základě jména pole:
SysDictTable dictTable;
Common common;
FieldId fieldId;
;
dictTable = new SysDictTable(common.TableId);
fieldId = dictTable.fieldName2Id('Name');
if (fieldId)
{
   info(common.(fieldId));
}
Užitečný odkaz: MSDN: Intrinsic Functions

sobota 18. července 2009

SysMultiTableLookup

Tento týden jsem potřeboval vytvořit lookup s poli z různých tabulek. Věděl jsem o existenci třídy SysMultiTableLookup, ale nějak jsem ji dříve nepotřeboval. A teď tedy přišel čas, abych se na ni podíval. Tuto třídu vytvořil Vanya Kashperuk (blog) s cíle rozšířit funkcionalitu široce používané třídy SysTableLookup. SysMultiTableLookup přináší možnost zobrazovat pole z více tabulek, používat display metody, agregační funkce nebo zadat jiný než standardní popisek pole. Vše, co jsem vyzkoušel, funguje velmi dobře. Skvělá práce! Třídu a formulář s příklady lze stáhnout zde.

neděle 12. července 2009

Reflexe - úvod

Reflexe (reflection) umožňuje pracovat se strukturou programu samotného. Když například Unit Testing Framework spouští všechny metody začínající "test", využívá k tomu reflexi. Hrátky s reflexí jsou zábavné a zároveň umožní tvořit velmi zobecněná řešení některých problémů, takže ji určitě neignorujte. :-) Obecně bych rozdělil využití reflexe v Axaptě do dvou hlavních oblastí: 1. Analýza aplikace - například:
  • vyhledání tříd implementujících dané rozhraní (Které RunBaseBatch třídy se mění aktuálním releasem?)
  • výpis polí tabulky pro uživatelský výběr (AIF, DEV_SysTableBrowser)
  • generátory kódu (metoda find() na základě primárního indexu)
  • atd.
2. Ovlivnění business logiky - tím míním např.:
  • volání metod na základě dat (Např. volám konkrétní metodu všech tříd, jejichž názvy jsem uložil do tabulky. To mi umožní měnit aplikační logiku dle aktuálních požadavků bez potřeby zásahu do kódu.)
  • provedení určité akce se všemi poli v tabulce, nebo třeba s poli určitého datové typu (validace)
  • atd.
Z funkčního hlediska existují dva přístupy:
  • TreeNode - představuje práci s AOT. Umožňuje procházet AOT, spouštět objekty, vytvářet, exportovat, pracovat s vrstvami atd. Základem je třída TreeNode, resp. její rozšíření SysTreeNode.
  • Dictionary - pracuje jen s datovým slovníkem (EDT, tabulky, třídy; ne třeba formuláře), ale objektům "rozumí" lépe než TreeNode. Pracuje s dědičností, přístupovými modifikátory (např. private), databází atd. Využívá systémové třídy Dict* a aplikační třídy SysDict*, v kterých jsou mnohá užitečná rozšíření.
Ještě se sluší poznamenat, že některé úkoly lze realizovat i jinak a mnohdy i efektivněji. Napadají mě třeba:
  • křížové reference (pokud jsou aktuální, samozřejmě)
  • UtilElements a další systémové tabulky
  • polymorfismus (jinak řečeno - řešení vhodným objektovým návrhem)
Na závěr dva jednoduché příklady, složitější aplikace si schovám na jindy. Ukázka 1 - Dictionary
Dictionary dictionary = new Dictionary();
DictClass dictClass;
ClassId classId;
;

classId = dictionary.classNext(0);

while (classId)
{
    dictClass = dictionary.classObject(classId);
    info(strFmt(
        "Třída %1 - instančních metod: %2",
        dictClass.name(),
        dictClass.objectMethodCnt()));
    classId = dictionary.classNext(classId);
}
Ukázka 2 - TreeNode
TreeNode formsNode = TreeNode::findNode(@'\Forms\'); //cesta v AOT
TreeNodeIterator iterator;
TreeNode formNode;
;

iterator = formsNode.AOTiterator();
formNode = iterator.next();

while (formNode)
{
     info(strFmt(
         "Formulář %1 - datových zdrojů: %2",
         formNode.AOTname(),
         formNode.AOTfindChild('Data Sources').AOTchildNodeCount()));
    formNode = iterator.next();
}

Videa

Dnes uvedu pár tipů na videa týkající se Axapty. Některé věci je prostě dobré vidět v chodu. Hlavním zdrojem je Channel 9 na MSDN. Dají se tam najít i např. rozhorvory s tvůrci, ale já jsem vybral linky na několik screencastů: Dynamics AX 2009 AIF Web Services Dynamics AX 4.0 - Unit Testing Exception handling in X++ Dynamics AX 4.0 - 3-tier Development Samozřejmě se dá něco najít i na známém YouTube (BTW není nad český překlad TyTrubko ;)), například: Dynamics AX Item Approval Workflow Setting Up Outlook Sychronization in Dynamics AX 2009 TabaxLite & Microsoft Dynamics Ax (TabaxLite je verze Tabaxu určená pro koncové uživatele) A nakonec zmíním tutoriály na MSDN: “How Do I” Videos — Dynamics AX PS: Celkem nedávno rozjel český Microsoft portál MSTV - přímo k Dynamics tam sice nic není, ale zato spousta zajímavostí ohledně .NETu...