Wednesday, April 29, 2009

Getting the stacktrace

Hi,


X++ has this little function built in the class xSession, which gives you the stacktrace. This may come in handy for you in special circumstances (fe together with the infolog of an error).

Syntax:
xSession::xppCallStack()

The function returns a container, so handle accordingly.
If you want to show the current stacktrace:
info(con2str(xSession::xppCallStack()));

Best of luck,


Willy.

Monday, April 27, 2009

Application Benchmarking Toolkit for Microsoft Dynamics AX

Hey all,

Microsoft has released a seperate, freely available solution to test your Ax 2009 system.

Check it out at

http://benchmarktoolkit.codeplex.com/


Released under open source license, approx. 11 Mb.

Happy stress and performance testing!


Willy

Sunday, April 26, 2009

Number of records in a form

Have you ever needed the number of rows that are returned by the executeQuery method on the datasource of a form? For example to show some progress display while you evaluate each record individually?



Not available in earlier versions of Dynamics Ax aka Axapta, but now it is:



yourdatasource_ds.numberOfRowsLoaded();



Easier then doing a select count statement, not?

(The MSDN help information (Ax 2009 SDK) is rather limited for this function.)


Greetings,



Willy.

Friday, April 24, 2009

List of modified AOT objects

You are in an upgrade process. You browse the AOT to check for modified elements. Sometimes it's easy to spot them (bold typeface, marked USR), but sometimes it's not (they seem to belong to the SYS-layer, but when expanded they reveal themselves as belonging to another layer).

This short script will give you a list of all "touched" objects.

static void CheckLayer(Args _args)
{ TreeNode TreeNode;
TreeNode BaseTreeNode;
UtilEntryLevel UtilEntryLevel;
#AOT
;
BaseTreeNode = TreeNode::findNode(#TablesPath);

TreeNode = BaseTreeNode.AOTfirstChild();

while(TreeNode)
{
UtilEntryLevel=treeNode.applObjectLayer();
if((UtilEntryLevel==UtilEntryLevel::usr) (UtilEntryLevel==UtilEntryLevel::var))
{
info(strfmt("Object %1 - layer %2",TreeNode.AOTname(),enum2str(UtilEntryLevel)));
}
TreeNode=TreeNode.AOTnextSibling();
}
}

This script gives you all the tables modified in either the VAR or USR layer.
Replace the #TablesPath macro name as needed, to get EDT, classes, forms, ...

Dynamics Ax provides some nice upgrade tools, but sometimes the "manual" approach is needed.

Kind regards,

Willy

Thursday, April 23, 2009

Missing stored procedures

When you upgrade Ax from V3 to V4 or from V3 to V2009, you might not be able to start the new AOS instance. The application event log may show an event similar like this:

Object Server 02: Internal Error occurred executing stored procedure when creating session for the AOS.

Cause:
The SQL remote procedures CREATESERVERSESSIONS and CREATEUSERSESSIONS are not created automatically in your upgrade database.

Solution:
1) Copy the stored procedures from a working version (database V4 or V2009, by using fe SQL Server Management Studio)
or
2) Use following scripts.

You can use the following scripts to create the necessary procedures:


USE [YourDatabaseNameGoesHere]
GO
/****** Object: StoredProcedure [dbo].[CREATESERVERSESSIONS] Script Date: 10/05/2008 15:15:32 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[CREATESERVERSESSIONS] @aosId varchar(50), @version int, @instanceName nvarchar(50), @recid bigint, @maxservers int, @status int, @loadbalance int, @workload int, @serverid int OUTPUT as declare @first as varchar(50) declare @max_val as int begin select top 1 @first = SERVERID from SYSSERVERSESSIONS WITH (UPDLOCK, READPAST) where STATUS = 0 and AOSID = @aosId and INSTANCE_NAME = @instanceName if (select count(SERVERID) from SYSSERVERSESSIONS where SERVERID IN (@first)) > 0 begin update SYSSERVERSESSIONS set AOSID = @aosId, VERSION = @version, INSTANCE_NAME = @instanceName, LOGINDATETIME = dateadd(ms, -datepart(ms,getutcdate()), getutcdate()), LASTUPDATEDATETIME = dateadd(ms, -datepart(ms,getutcdate()), getutcdate()), STATUS = @status, WORKLOAD = @workload where SERVERID IN (@first) and ((select count(SERVERID) from SYSSERVERSESSIONS where STATUS = 1 and LOADBALANCE = 0) < @maxservers) if @@ROWCOUNT = 0 select @serverid = 0 else select @serverid = @first end else begin if (select count(SERVERID) from SYSSERVERSESSIONS WITH (UPDLOCK) where STATUS = 1 and LOADBALANCE = 0) >= @maxservers select @serverid = 0 else begin if (select count(SERVERID) from SYSSERVERSESSIONS) = 0 select @max_val = 1 else select @max_val = max(SERVERID)+1 from SYSSERVERSESSIONS insert into SYSSERVERSESSIONS(SERVERID, AOSID, INSTANCE_NAME, VERSION, LOGINDATETIME, LASTUPDATEDATETIME, STATUS, RECID, LOADBALANCE, WORKLOAD) values(@max_val, @aosId, @instanceName, @version, dateadd(ms, -datepart(ms,getutcdate()), getutcdate()), dateadd(ms, -datepart(ms,getutcdate()), getutcdate()), @status, @recid, @loadbalance, @workload) select @serverid = @max_val end end end


And


USE [YourDatabaseNameGoesHere]
GO
/****** Object: StoredProcedure [dbo].[CREATEUSERSESSIONS] Script Date: 10/05/2008 15:17:32 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[CREATEUSERSESSIONS] @clientType int, @sessionType int, @serverid int, @versionid int, @userid varchar(5), @lanExt varchar(10), @manExt varchar(10), @sid varchar(124), @recid bigint, @startId int, @maxusers int, @licenseType int, @masterId int, @maxClientId int, @sessionid int OUTPUT, @loginDateTime datetime OUTPUT as declare @return_val as int declare @first as int declare @max_val as int begin select @sessionid = -1 select @max_val = -1 select @loginDateTime = dateadd(ms, -datepart(ms,getutcdate()), getutcdate()) if(not exists(select * from SYSSERVERSESSIONS WITH (NOLOCK) where SERVERID = @serverid AND Status = 1)) begin select @sessionid = -2 return end select @first = min(SESSIONID) from SYSCLIENTSESSIONS WITH (UPDLOCK,READPAST) where STATUS = 0 and SESSIONID > @maxClientId and SESSIONID <> @masterId if (select count(*) from SYSCLIENTSESSIONS where SESSIONID IN (@first)) > 0 begin if (@licenseType = 0) begin update SYSCLIENTSESSIONS set STATUS = 1, VERSION = @versionid, SERVERID = @serverid, USERID = @userid, LOGINDATETIME = @loginDateTime, SID = @sid, USERLANGUAGE = @lanExt, HELPLANGUAGE = @manExt, CLIENTTYPE = @clientType, SESSIONTYPE = @sessionType where SESSIONID IN (@first) end else if (@licenseType = 1) begin update SYSCLIENTSESSIONS set STATUS = 1, VERSION = @versionid, SERVERID = @serverid, USERID = @userid, LOGINDATETIME = @loginDateTime, SID = @sid, USERLANGUAGE = @lanExt, HELPLANGUAGE = @manExt, CLIENTTYPE = @clientType, SESSIONTYPE = @sessionType where SESSIONID IN (@first) and ((select count(SESSIONID) from SYSCLIENTSESSIONS where CLIENTTYPE = @clientType and ((STATUS = 1) or (STATUS = 2))) < @maxusers) end else if (@licenseType = 2) begin update SYSCLIENTSESSIONS set STATUS = 1, VERSION = @versionid, SERVERID = @serverid, USERID = @userid, LOGINDATETIME = @loginDateTime, SID = @sid, USERLANGUAGE = @lanExt, HELPLANGUAGE = @manExt, CLIENTTYPE = @clientType, SESSIONTYPE = @sessionType where SESSIONID IN (@first) and ( (select count(SESSIONID) from SYSCLIENTSESSIONS where CLIENTTYPE = @clientType and (USERID = @userid) and ((STATUS = 1) or (STATUS = 2))) > 0 or (select count(distinct USERID) from SYSCLIENTSESSIONS where CLIENTTYPE = @clientType and ((STATUS = 1) or (STATUS = 2))) < @maxusers ) end if @@ROWCOUNT = 0 select @sessionid = 0 else select @sessionid = @first end else begin if (@licenseType = 1) begin if (select count(SESSIONID) from SYSCLIENTSESSIONS where CLIENTTYPE = @clientType and ((STATUS = 1) or (STATUS = 2))) >= @maxusers select @sessionid = 0 end else if (@licenseType = 2) begin if ( ((select count(distinct USERID) from SYSCLIENTSESSIONS where CLIENTTYPE = @clientType and ((STATUS = 1) or (STATUS = 2))) >= @maxusers) and ((select count(SESSIONID) from SYSCLIENTSESSIONS where CLIENTTYPE = @clientType and (USERID = @userid) and ((STATUS = 1) or (STATUS = 2))) = 0) ) select @sessionid = 0 end if (@sessionid = -1) or (@licenseType = 0) begin if (select count(SESSIONID) from SYSCLIENTSESSIONS WITH (UPDLOCK) where STATUS = 0 or STATUS = 1 or STATUS = 2 or STATUS = 3) = 0 select @max_val = @startId else select @max_val = max(SESSIONID)+1 from SYSCLIENTSESSIONS WITH (UPDLOCK) if (@max_val > 65535) select @sessionid = -3 else begin insert into SYSCLIENTSESSIONS(SESSIONID, SERVERID, VERSION, LOGINDATETIME, USERID, SID, USERLANGUAGE, HELPLANGUAGE, CLIENTTYPE, SESSIONTYPE, RECID, CLIENTCOMPUTER, STATUS) values(@max_val, @serverid, @versionid, @loginDateTime, @userid, @sid, @lanExt, @manExt, @clientType, @sessionType, @recid, '', 1) if @@ROWCOUNT = 0 select @sessionid = -1 else select @sessionid = @max_val end end end end


Don't forget to replace the dummy databasename at the start of the procedure (YourDatabaseNameGoesHere).

Good luck,

Willy.

Wednesday, April 22, 2009

The Main Menu is dead, long live the Main Menu

In Ax 2009, the user interface underwent some drastic changes. The victim seems to be the MainMenu.
Do you still like the old style Ax (Axapta)? Fond memories of the MainMenu?
Here is a simple job, that shows the classic MainMenu.

static void ShowMainMenu(Args _args)
{ Menu Menu;
#admin
;
Menu = new Menu(#MainMenu);
Menu.AOTrun();
}

This should get you going to show the main menu at startup.

Greetings,
Willy

Tuesday, April 21, 2009

Fellow bloggers

There are some interesting blogs about Dynamics Ax out there.
Some updated frequently, some less frequently, but none the less very interesting.

Just to get you started:

http://palleagermark.blogspot.com/
http://gatesasbait.spaces.live.com/
http://dynamicsax-dev.blogspot.com/
http://dynamics-ax.blogspot.com/
http://www.crankturner.com/
http://daxguy.blogspot.com/

Happy reading!


Willy