Jak tedy donutit AX2009, aby se spokojila se správou verzí bez Team Serveru?
Odpověď je jednoduchá:
SysVersionControlSystem.requireTeamServer()
musí vracet false
.Sám jsem čekal něco komplikovanějšího… :-)
Dynamics AX (Axapta) a související technologie z pohledu programátora
SysVersionControlSystem.requireTeamServer()
musí vracet false
.![]() |
Kontextové menu TortoiseSVN |
![]() |
Ikony souborů |
![]() |
Ukládání do repozitáře |
![]() |
Historie verzí |
file://ServerName/path/to/repos/
nebo
file:///C:/repo
). Těchto pracovních adresářů můžete mít kolik chcete a můžete si nějaký vytvořit kdykoli, kdy potřebujete svá data a máte dokážete číst z adresáře s repozitářem (serverový disk přístupný přes VPN je skvělá volba).SysDictTable::isTableMapped()
; konkrétní přiklad by vypadal takto:if (SysDictTable::isTableMapped(tableNum(PBAItemLine), _args.dataset()))
isTableMapped()
je trochu ošklivá (dívá se přímo do AOT na obsah uzlu Mappings), inteligentní podpora zřejmě v API chybí.interface Rozhrani { int rychlost(int _rychlost){} }Předpokládal bych, že každá třída implementující toto rozhraní musí obsahovat metodu
rychlost()
s parametrem a návratovou hodnotou typu int
.main()
i bez chyby proběhne, včetně přiřazení do proměnné.class ImplementujiciTrida implements Rozhrani { //Správný název metody, ale nesprávný typ parametru i návratové hodnoty void rychlost(str _slovniPopisRychlosti) {} public static void main(Args _args) { Rozhrani trida = new ImplementujiciTrida(); //Instance nemá metodu akceptující číselný parameter a vracející hodnotu, //přesto toto volání proběhně. int i = trida.rychlost(5); } }Metoda
rychlost()
obdrží hodnotu překonvertovanou do řetězce a vrátí hodnotu 0 (případná hodnota v proměnné i v main()
by byla přepsána), přestože má návratový typ void
.rychlost()
obsahovala parametr, pro který neexistuje implicitní konverze (např. Object _o
), dojde k chybě za běhu aplikace.DictClass.extend()
nebo MSDN. handle()
) a čtyři překrytelné (equal()
, toString()
, wait()
a xml()
). Znamená to tedy, že každý objekt obsahuje tyto metody? Překvapivě… nikoli.toString()
. Takový kód vůbec nejde zkompilovat! Pokud tuto instanci přiřadíte do proměnné typu Object, přiřazení je sice korektní a Object obsahuje metodu toString()
, takže kompilace proběhne v pořádku, nicméně volání metody vyhodí runtime výjimku.Object o = new MyClass();
o.toString(); //runtime chyba
Metodu toString()
můžete ve své třídě sami naimplementovat a kód bude pochopitelně fungovat. Ale pozor - zde vytváříte nové metody, nepřekrýváte ty ze třídy Object. To má dva zřejmé efekty - zaprvé nemá smysl snažit se volat super()
(to platí i pro konstruktor(!)) a za druhé můžete vytvořit metodu stejné signatury jako má některá z finálních metod třídy Object.extends Object
v classDeclaration
vaší třídy. Pak můžete na jejích instancích volat všechny metody zděděné z Object, překrytelné metody se objeví v kontextové nabídce (viz obrázek), můžete volat jejich implementaci ve třídě Object pomocí super()
a tak dále.extends Object
používají zřejmě veškeré systémové třídy (tzn. třídy v
uvedené v System Documentation) a také některé třídy definované v X++, například RunBase.
extends Object
zcela stejně. Popsané chování je také zcela v rozporu s MSDN dokumentací, která například říká: "Methods in the Object class can be called for any object.", tedy "Metody ve třídě Object mohou být volány pro libovolný objekt.".equal()
), protože to prostě není nijak zaručenohandle()
- a znamená to, že ani na finální metody třídy Object se nelze stoprocentně spolehnout.extends Object
.
Konference Convergence 2010 Europe se koná v říjnu v Londýně, Haagu a 18.-19.10. také v Praze. Od dnešního dne je otevřena registrace, tak se pojďme podívat, co se bude dít.
Pondělí 18. října je určeno pro Microsoft partnery, řeč bude pochopitelně o plánech do budoucna, o nových produktech a také o Microsoft Partner Network (program je na PartnerSource).
Úterý 19. října je otevřeno všem (platícím), ohledně AX bude na programu například AX for Retail, upgrady AX nebo archivace dat. Krom AX bude řeč také o Dynamics NAV a CRM (program zde).
Webové stránky akce naleznete zde. Cena je 100€ za Partner Day, 299€ za druhý den a 374€ za oba (bez DPH).
Já osobně se tam nejspíš neobjevím, ale můžete mě potkat za dva týdny na Microsoft Days 2010!
Nedávno jsem si konečně našel čas na vyzkoušení produktu s (opravdu dlouhým) názvem Microsoft Dynamics AX 2009 Management Pack for System Center Operations Manager 2007. System Center Operations Manager (SCOM) slouží k monitorování počítačů - dokáže vyhledat požadované objekty (třeba aplikační servery) v síti, sledovat stav služeb, různých parametrů včetně výkonnostních, spouštět automatizovaně reakce na události a tak podobně.
![]() |
Hlavní okno SCOM 2007 |
Pro SCOM existuje celá řada balíčků (tzv. Management Packs), které obsahují specializované objekty pro jednotlivé produkty, například Windows Server OS, SQL Server nebo třeba SharePoint. Podpora pro Dynamics AX 2009 je tedy samozřejmě implementována právě v podobě takového Management Packu.
Instalace
Instalace byla trochu problematická, protože moje virtuální AX prostředí je postavené nad SQL Serverem 2008 R2, který není SCOM 2007 R2 podporován (více zde). Nicméně se mi podařilo tento problém obejít pomocí přímého zavolání DBCreateWizard ([Instalační disk]:\SupportTools\i386\DBCreateWizard.exe) a vše funguje bez problémů. Vytvořil jsem doménový účet pro Action Account a úspěšně dokončil instalaci SCOM.
Dalším nezbytným krokem je přidání management packů. Ty stáhnete například z Microsoft katalogu; přímý link na Dynamics AX Management Pack je zde.
Postup instalace management packu do SCOM je pak následující:
Zde jen krátce poznamenám, že některé balíky obsahují management packy pro více verzí produktu, takže např. pro Windows Server 2008 je nutné nainstalovat objekty společné pro všechny verze (Microsoft.Windows.Server.Library.mp) a pak (nebo zároveň) management packy specifické pro Win Server 2008.
Konfigurace Dynamics AX Management Packu obnáší ještě několik kroků. Postupoval jsem takto:
Použití
SCOM začne v pravidelných intervalech hledat AOS v síti (to jsem si nevyzkoušel, pracoval jsem jen s jedním serverem). Informace o nalezených instancích jsou pak zobrazeny v Monitoring > Microsoft Dynamics AX 2009. Zde naleznete informace o stavu jednotlivých aplikací, o přihlášených uživatelích, batch úlohách, o důležitých událostech. Přímo z tohoto místa je také možné ručně spouštět a zastavoval aplikační servery.
Podívejte se na pár obrázků:
AOS úspěšně běží, 2 uživatelé jsou on-line |
![]() |
Varování: aplikační server se zastavil |
![]() |
Server (po manuálním spuštění z prostředí SCOM) úspěšně naběhl. Mimochodem, Task Output nápadně připomíná PowerShell. |
![]() |
V sekci Administration lze nastavovat upozornění na určité události (e-mail, IM zpráva apod.), v Authoringu lze měnit chování mnoha objektů, psát skripty reagující na události a tak dále. SCOM také nabízí Management Shell, což je PowerShell rozšířený o řadu cmdletů potřebných pro práci se SCOM objekty.
Musím se přiznat, že jsem od Management Packu pro Dynamics AX očekával trochu více, zejména co se týče akcí přímo spustitelných z prostředí SCOM. Na druhou stranu, SCOM jako takový se mi rozhodně zalíbil a je třeba vzít v úvahu, že správa Dynamics AX zahrnuje také databázové servery, hlídání místa na discích, Active Directory a tak dále. A v tom všem může být SCOM extrémně užitečný. Pozitivně hodnotím také rozšiřitelnost, včetně možnosti vytvářet vlastní management packy.
Vyhledávač datových referencí je nástroj pro AX, jehož primitivní podobu jsem vyvinul pro upgrade v Británii. Bylo totiž potřeba přesunout celý jeden modul do jiné vrstvy a krom změn ID objektů (což není problém) bylo také třeba zjistit, zda jsou původní ID použita někde v databázi (typicky relace alternativních adres nebo řízení dokumentů). Právě pro toto hledání jsem si napsal program, vlastní aktualizaci jsem pak implementoval jako data upgrade script.
Protože jsem měl spoustu jiné práce a také dovolenou, dotažení implementace a refaktoring se trochu protáhli, nicméně nyní považuji práci za dokončenou. Výjimkou je podpora různých verzí AX, protože nyní je připravena pouze verze pro AX2009. To je sice u nástroje primárně určeného pro upgrady trochu nevýhoda, ale rád bych provedl základní testování na jedné verzi a pak teprve vytvořil paralelní verze pro starší Axapty. Pár věcí podporovaných AX2009 ve starších verzích chybí, je tedy třeba udělat nějaké změny v kódu.
Kolega Dan, který mi dělal alfa-testera, se hlasitě dožadoval dokumentace, což jsem nakonec vyslyšel. Pro podrobnější popis programu tedy odkazuji na dokumentaci (odkaz níže). Dokumentace zatím nebyla nikým testována, nejvíc chyb proto očekávám tam.
Pokud se rozhodnete Vyhledávač datových referencí vyzkoušet a naleznete nějakou chybu, nezapomeňte mi dát vědět. Předem díky!
Odkazy
Dnes mě vyvedl z konceptu kolega, který potřeboval převést řetězcovou reprezentaci reálného čísla na typ real. Problém byl, jak jinak, ve formátu čísla. Já jsem ovšem také netušil, že ačkoli funkce num2str() umožňuje zadat znak pro oddělení tisíců a pro oddělení desetinných míst, inverzní funkce str2num() nic takového nepodporuje. Oddělovač tisíců (",") tedy konverze považovala za desetinnou čárku a bylo zle. Dost nás překvapilo, že jsme přímo v Axaptě žádné rozumné řešení nenašli (prosté nahrazení tečky čárkou nepovažuji za dostatečně robustní).
Záchranu jsme nakonec našli v System.Single::Parse().
Jedním z důležitých úkolů při nasazování AX je správné nastavení přístupových práv. Pro porozumění tomu, jaké security klíče je třeba nastavit, vytvořil Microsoft nástroj nazvaný Security Profiler.
Security Profiler je implementován v X++ a jeho použití je velmi jednoduché - administrátor spustí formulář Security Profiler a a klikne na Start Profiling. Od této chvíle se zaznamenávají potřebné security klíče, administrátor tedy provede nějaké akce (spuštění formuláře, tisk sestavy) a ukončí sledování tlačítkem Stop Profiling. Výsledkem je seznam security klíčů, které byly pro akci nějakým způsobem vyžadovány - a které je třeba vzít v potaz při nastavování uživatelských účtu s omezenými právy.
Security Profiler je zatím bohužel ve vývoji a není normální cestou k dispozici, lze si ho ale vyžádat přes support. Nicméně je pravděpodobné, že v budoucnu bude standardní součástí Dynamics AX.
O něco více informací (včetně obrázků) lze najít zde.
Čas od času se setkávám s představou, že AX aplikaci není třeba kompilovat jako celek a že kompilace změněných objektů je dostačující. Na takových projektech pak dochází k záhadnému chování aplikace, na chyby se přichází až v průběhu nasazování a podobně. Jak je to možné?
Je to proto, že změněné objekty mají další závislosti. Například:
Nejjednodušší způsob, jak tyto problémy odhalit (a některé i vyřešit), je pravidelně kompilovat celou aplikaci (a kontrolovat výsledky!). Ačkoli lze kompilaci spouštět manuálně, pro pravidelný běh je daleko výhodnější použít plánovanou úlohu, například takto:
Ax32.exe konfigurace.axc -logdir=C:\log -startupCmd=CompileAll_+
Následovat může synchronizace databáze, odeslání výsledků kompilace e-mailem a podobně. Kompilační log lze naimportovat zpět do AX, na druhou stranu s ním občas trochu bojují webové prohlížeče (je to totiž XML s transformací do HTML).
Podíváme-li se ještě jednou na příklady 1. a 2., vidíme, že jde o chyby vývojáře. Aby bylo možné tyto chyby minimalizovat, je nezbytně nutné udržovat aktuální křížové reference. Bylo by ale naivní předpokládat, že na projektech, kde nepovažují za nutné aplikaci ani kompilovat, pravidelně aktualizují křížové reference. Osobně považuji prostředí bez aktuálních referencí za nezpůsobilé k vývoji.
Zrovna dnes mi na jednom projektu zakázal jakýsi tamní Project Engineer zkompilovat aplikaci s tím, že jde o velmi neobvyklý krok. Pokud je pro ně neobvyklý, není to dobré znamení. Na posledním projektu, kde mi tvrdili, že kompilace je zbytečná, selhalo hned první nasazení právě kvůli nezkompilované rodičovské třídě…
Od května platí pro nové požadavky pro dosažení kompetencí v Partner programu. To sice není zrovna oblast mého hlavního zájmu, nicméně vzhledem k mírné panice mezi některými partnery považuji za vhodné alespoň upozornit, že se něco takového děje.
Celý proces je v přechodné fázi a tak pro stejnou kompetenci třeba platí různé požadavky nyní a od října tohoto roku. Každá kompetence má svůj vyšší level, Advanced Competency, ale ty budou také dostupné až v říjnu.
Informace o požadavcích, benefitech, mapování současných kompetencí na nové atd. naleznete v dokumentu The Value of Earning a Microsoft Competency. Kompetence ERP začíná na straně 36, ISV na straně 51.
Pro Dynamics AX kompetenci se počítají lidé, kteří mají všech pět následujících certifikací (požadavky na verze jsou velmi mírné - počítá se i třeba AX 2.5, ale jen do října!):
Takže mě čekají SQL Server a finance…
Dictionary dict = new Dictionary();
TableId tableId;
;
setPrefix("Synchronizace DB");
tableId = dict.tableNext(0);
while (tableId)
{
setPrefix(tableId2Name(tableId));
try
{
appl.dbSynchronize(tableId, false);
}
catch {}
tableId = dict.tableNext(tableId);
}
Tyto komplikace vidím to jako další argument, proč raději upgradovat přímo na AX2009…
Základní informace o AOS zjistím se zadaného .axc souboru, instanci služby pomocí Get-Service a cestu k aplikačnímu adresáři z registrů počítače, kde je nainstalované AOS. Z toho také plyne, že je třeba mít nakonfigurovaný PowerShell remoting. Podrobnosti zde nebudu rozepisovat, zájemci si pročtou kód a nezájemci ušetří čas :-). Skript stahujte zde.AppLocalPath : C:\Program Files\Microsoft Dynamics AX\50\Application\Appl\JmenoAplikace AosName : JmenoAOS AosNumber : 03 AosServiceInstance : System.ServiceProcess.ServiceController AosPort : 2714 AosComputerName : JmenoPocitaceSAos.domena AppUncPath : \\
JmenoPocitaceSAos
\C$\Program Files\(...)\Appl\
JmenoAplikace
queryBuildRange.value(SysQueryRangeUtil::day());
To by nebylo nic zajímavého, vtip je v tom, že je lze použít i při filtrování dotazu pomocí dialogu (na formuláři, v RunBase třídách a podobně):
AxUtil create /model:"My Model" /Layer:USR
Všimněte si, že model je vytvářen v konkrétní vrstvě. Bylo by také možné určit databázi, vůči které se příkaz provede.
Výběr modelu
Obdobně jako vrstvu lze učit také výchozí model. Model ale může být změněn, narozdíl od vrstvy, i za běhu aplikace. Zvolit lze samozřejmě jen model patřící do aktuální vrstvy.
Aktivní model je zobrazen ve stavovém panelu (a je ho odtud možné i změnit).
AxUtil export /model:"My Model" /file:MyModel.axmodel
Velkou výhodou exportovaného modelu je manifest. Manifest je obsažen přímo v souboru s modelem a obsahuje informace jako je jméno modelu, jeho verze, zdrojová vrstva a podobně.
Manifest lze zobrazit:
AxUtil list /model:"My Model"
a případně změnit:
AxUtil edit /model:"MyModel" /manifest:Version="2.0.0.0",Publisher="MFP",Description="My first model"
Exportovaný model lze také opatřit elektronickým podpisem. Podpis pak může využít například zákazník k ověření, zda nebyl soubor během přenosu mofikován.
Import
Modely uložené do souboru je pochopitelně možné importovat do AX:
AxUtil import /file:MyModel.axmodel
Do jedné vrstvy lze importovat více modelů, například řešení od dvou různých dodavatelů, což je dnes vcelku nepříjemný úkol (a noční můra při upgradech). Při takovém importu se mohou vyskytnout dva hlavní problémy:
AxUtil delete /model:"My Model"
Závěr
Mfp uvádí o něco více podrobností, takže v případě zájmu navštivte jeho blog. Nicméně dokud není AX 6 hotova, stejně není nic definivní. Až bude zveřejněna, určitě se na modely a jejich implementaci podívám podrobněji.
Dialog dialog = new Dialog("Dimenze");
;
dialog.addField(typeId(Dimension));
dialog.run();
Pokud je dále třeba nastavit nějakou vlastnost, například AllowEdit, nabízel by se takovýto zápis:
Dialog dialog = new Dialog("Dimenze");
DialogField field;
;
field = dialog.addField(typeId(Dimension));
field.allowEdit(false);
dialog.run();
Jenže… výsledek není správný. Znepřístupněné je jen první políčko:
Dialog dialog = new Dialog("Dimenze");
DialogField field;
SysDictType dictType;
FormBuildStringControl control;
int i;
;
field = dialog.addField(typeId(Dimension));
dictType = new DictType(typeId(Dimension) >> 16);
for (i = 1; i <= dictType.arraySize(); i++)
{
control = dialog.dialogForm().buildControl(field.fieldname(i));
control.allowEdit(false);
}
dialog.run();
Výsledný dialog pak má všechny dimenze neaktivní, jak bylo požadováno.
$connectorDllPath = 'C:\Program Files\Microsoft Dynamics AX\50\' `Druhý skript slouží k výběru konfigurace AX a jméno této konfigurace předává prvnímu skriptu. Seznam konfigurací je zobrazen v grafickém okně, protože kliknutí mi přišlo jako nejrychlejší způsob výběru. GUI je vytvořeno pomocí WPF (Windows Presentation Foundation) a vypadá takto (s tím rozdílem, že já tam mám konfigurací celkem 42):+ 'Client\Bin\Microsoft.Dynamics.BusinessConnectorNet.dll' $configurationPath = 'D:\CONFIGURATIONS' $configurationName = $args[0]
$configFile = Join-Path $configurationPath $configurationName if (!(Test-Path $configFile)) { throw "Konfigurace $configFile nebyla nalezena" } #zalogování do AX pomocí Business Connectoru
try
{
[void][reflection.Assembly]::Loadfile(
$connectorDllPath
) $ax = New-Object Microsoft.Dynamics.BusinessConnectorNet.Axapta $ax.logon('', '', '', $configFile) } catch { throw 'Přihlášení pomocí Business Connectoru se nezdařilo' } #objekty představující AX tabulky
$sysClientSessions = $ax.CreateAxaptaRecord('SysClientSessions'); $sysServerSessions = $ax.CreateAxaptaRecord('SysServerSessions'); #dotaz na aktivní uživatele přihlášené přes GUI
$sysClientSessions.ExecuteStmt('select * from %1 where %1.Status == 1 ' ` + '&& %1.ClientType == 0'); if (!$sysClientSessions.Found) { '0 aktivních uživatelů' }
#iterace přes uživatele, získaní podrobných informací a zabalení do objektu while ($sysClientSessions.Found)
{ $userId = $sysClientSessions.get_Field('UserId') $userName = $ax.CallStaticClassMethod('UserInfoHelp', 'userName', $userId)
$sysServerSessions.ExecuteStmt('select * from %1 where %1.ServerId == ' ` + $sysClientSessions.get_Field('ServerId')) $aosInstanceName = $sysServerSessions.get_Field('AOSId')
#
zabalení do objektu
New-Object PSObject -Property @{ AosName = $aosInstanceName UserId = $userId
UserName = $userName} [void]$sysClientSessions.Next();
}
#odhlášení z AX [void]$ax.logoff()
Get-ChildItem
(alias dir
).
Zvolená konfigurace
($listBox.SelectedItem
) je použita jako parametr pro výše uvedený skript a daný kód je umístěn v obsluze kliknutí na tlačítko ($button.add_Click()
).
$configurationPath = 'D:\CONFIGURATIONS'Protože WPF okno vyžaduje běh v STA módu (Single-Threaded Apartment; neznám smysluplný překlad), nelze tento skript spustit běžným způsob. Jednou (a nejjednoduší) možností je zavolání PowerShellu s parametrem -STA, takže spuštěcí příkaz lze napsat (a uložit do .lnk) třeba takto:Add-Type –assemblyName PresentationFramework Add-Type –assemblyName PresentationCore
Add-Type –assemblyName WindowsBase
$window = New-Object Windows.Window
$window.Title = 'AX konfigurace'
$window.SizeToContent = 'WidthAndHeight'
$label = New-Object Windows.Controls.Label $label.Content = 'Seznam konfigurací:' $listBox = New-Object Windows.Controls.Listbox $listBox.ItemsSource = (% {
Get-ChildItem
$configurationPath -Include *.axc -Name}) $button = New-Object Windows.Controls.Button $button.Content = 'Zobraz přihlášené uživatele'
$button.add_Click( { if ($listBox.SelectedItem) { $window.Hide()
Write-Host
$listBox.SelectedItem
try { #volání samostatného skriptu na získání informace o uživatelích $activeUsers = (absolutníCesta\prvníSkript.ps1 $listBox.SelectedItem) }
catch
{
Write-Host
$error
[0] -ForegroundColor Redexit
} }
}) $stackPanel = New-Object Windows.Controls.Sta
ckPanel
$stackPanel.Orientation = 'Vertical'
[void]
$stackPanel.Children.Add($
label
)
[void]
$stackPanel.Children.Add(
$listbox
)
[void]
$stackPanel.Children.Add(
$button
)
$window.Content = $stackPanel
[void]
$window.ShowDialog() $textbox.Text $activeUsers
C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe
-STA
-noexit
-command cesta\jmenoSkriptu.ps1