neděle 2. srpna 2009

Vyhledávání

Vyhledávání v kódu nebo v AOT je velice častá činnost. Hledáme použití proměnné, modelový kus kódu, objekty určitých vlastností atd. Přesto řada lidí neovládá některé užitečné funkce, které pro to Axapta nabízí. A právě na ty se nyní podíváme. Spustit formulář pro vyhledávání v AOT jistě dokáže každý, ale tento formulář umí podstatně více než jen vyhledat zadaný řetězec. Přímo na formuláři jsou další pole pro upřesnění kritérií (ačkoli pro některé jsem dosud nenašel smysluplné využití) a dokonce možnost zapsat vlastní funkci, která se spustí pro každý TreeNode (jaké má mít metoda parametry se dočtete v nápovědě k formuláři). Co už není tak očividné je možnost zadat regulární výraz do vyhledávacích polí. Syntaxe odpovídá metodě match() a je u ní zdokumentovaná. Takže např. následující příklad hledá přiřazení do proměnné i - jinak řečeno, proměnnou i následovanou libovolným počtem mezer a znakem =. Dohromady i: *=. Obdobně lze hledat třeba metody, jejichž název odpovídá určitému regulárnímu výrazu (např. <check.*sales pro metody začínající check a pak kdekoli obsahující sales). V tomto případě pochopitelně umístíme regulární výraz do pole (Nazváno) Named. Regulární výrazy lze využít i při vyhledávání labelů (což je mimochodem funkce k nezaplacení) a také v editoru kódu. V editoru hlavně pozor na to, že se vyhledává od pozice kurzoru do konce, to je dost matoucí. A kupodivu hledání v AOT a hledání v editoru pracuje jinak např. s koncem řádku… Při hledání v AOT lze využít ještě jednu zajímavou funkci a to vyhledávání ve vlastnostech. Nejprv je nutné přepnout volbu Hledat (Search) na Vše (All nodes) - tím se zobrazí další záložka: Vlastnosti (Properties). Na ní si pak najdeme patřičné vlastnosti, označíme jako Selected a zadáme hodnotu. Ta má opět hodnotu regulárního výrazu. Může to vypadat třeba takto:

Reflexe - AOT

Třída TreeNode představuje uzel AOT, tedy objekty, metody, pole ale i "adresáře" jako třeba Forms. S její pomocí pak můžeme provádět akce, na jaké jsme zvyklí při ruční práci s AOT. Například tento kód vytvoří novou třídu a spustí ji:
#AOT
TreeNode nodeClasses = TreeNode::findNode(#ClassesPath);
TreeNode nodeClass;
TreeNode nodeMethod;
;

nodeClass = nodeClasses.AOTadd('NovaTrida');
nodeMethod = nodeClass.AOTadd('main');
nodeMethod.AOTsetSource(
   'public static void main(Args _args){;info("Bezi");}', true);
nodeClass.AOTsave();
nodeClass.AOTcompile();
nodeClass.AOTrun();
Příklad asi nepotřebuje mnoho komentáře. Makro #AOT obsahuje cesty k různým prvkům (nebo části cest, jako třeba '\%1\\Fields\\\%2'). Skupinu všech tříd, novou třídu i její metodu reprezentuje třída TreeNode. Díky této funkcionalitě lze například snadno vytvářet generátory kódu apod. Další užitečná věc je práce s vlastnostmi (Properties). Vlastnost vyhledáme podle jejího názvu a v tom nám pomůže další makro - #Properties.
#AOT
#Properties
TreeNode nodeBOM = TreeNode::findNode(#TablesPath + #AOTDelimiter + tableStr(BOM));
;
info(nodeBOM.AOTgetProperty(#PropertyCachelookup));
Obdobně lze vlastnosti i nastavovat:
tableNode.AOTsetProperty(
   #PropertyFormRef,
   menuitemDisplayStr(Form1));
tableNode.AOTsave();
Často je také potřeba procházet iterovat přes větší množství AOT uzlů. K tomu se hodí tyto třídy:
  • TreeNode - metody AOTfindChild(), AOTfirstChild(), AOTnextSibling() atd.
  • TreeNodeIterator - prochází přímé potomky. Lze získat voláním AOTiterator() na TreeNode.
  • TreeNodeTraverser - prochází potomky včetně nepřímých.
  • TreeNodeTraverserSource - potomek TreeNodeTraverser; prochází jen uzly obsahující zdrojový kód.
Některé zajímavé metody pro práci s uzly AOT naleznete ve tříde SysTreeNode. Je jasné, že TreeNode obsahuje funkcí daleko více, ale pro základní představu to stad stačí.

ú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...