Greg On Dynamics Ax

Random thoughts about development using Dynamics AX and .Net

Archive for the ‘x++ Language’ Category

X++ for .Net Developers (.Net Rocks! Interview)

leave a comment »

I recently had an interview with Carl and Richard at .Net Rocks about what .Net developers need to know about AX.

The show went live yesterday – you can check it out here: http://www.dotnetrocks.com/default.aspx?showNum=703.

Written by gregondax

October 5, 2011 at 7:41 am

Posted in .Net, x++ Language

Refactoring a long parameter list of boolean flags

with 3 comments

If you have had the need to have a method accept many boolean arguments, you might design the method to accept a number of optional boolean parameters, such as:

public void print(boolean _proforma = false, boolean _hideFooter = false, boolean _hideHeader= false, boolean _archive = false)

This is not the best design. For a start if you are happy to accept all the defaults but want to archive the printout you’ll need to call the method like this:

print(false, false, false, true);

It also suffers from what refactoring practitioners call a “bad smell” – this one being a long parameter list, which makes it harder to read and understand, and gets worse over time as new arguments are added.

A refactoring to improve the design might be to introduce a (single) settings object to replace the parameters.
If you consider that to be overkill you may want to consider this alternative: replace the parameters with a single base Enum type:

Each enumeration element then has a value that maps to the two base binary. So the values for this base enum would be:

  • Proforma – 1
  • HideFooter – 2
  • HideHeader – 4
  • Archive – 8


This enables us to declare our print method like so:

public void print(DEV_PrintOptions _opts)
{
if ((_opts & DEV_PrintOptions::Proforma) == DEV_PrintOptions::Proforma)
// Do proforma stuff

if ((_opts & DEV_PrintOptions::HideFooter) == DEV_PrintOptions::HideFooter)
// Hide the footer

if ((_opts & DEV_PrintOptions::HideHeader) == DEV_PrintOptions::HideHeader)
// Hide the header

if ((_opts & DEV_PrintOptions::Archive) == DEV_PrintOptions::Archive)
// Archive the printout
}

Which allows the method to be called in a more flexible way, specifying only the options you wish to override, for example:

// print out proforma and archive it (accepting the default to print the footer and header):
print(Dev_PrintOptions::Proforma | Dev_PrintOptions::Archive);

Written by gregondax

February 26, 2010 at 9:15 am

Automated Messaging to Office Communicator (from code)

leave a comment »

A new feature introduced with Dynamics AX 2009 was integration with Office Communicator.

This enables the presence of a person/contact to be shown as part of the contact information, along with links to contact that person:

Office Communicator integration with Dynamics AX

Office Communicator integration with Dynamics AX

Selecting the link to send an instant message to a contact launches the Office Communicator client conversation:
Office Communicator Client

The following job shows how to send an instant message to a contact using Office Communicator from code:

static void UCMAExample(Args _args)
{
    AlertSender.OCS_Sender ocSender;
    ;

    try
    {
        ocSender = new AlertSender.OCS_Sender(@"sip:Administrator@contoso.com", "ax-srv-03.contoso.com",
                                                        "Administrator", @"thepassword", "contoso");
        ocSender.SendMessage("sip:Alicia@contoso.com", "Hello from AX", "Normal");
    }
    catch(Exception::CLRError)
    {
        throw error(AifUtil::getClrErrorMessage());
    }
}

To enable this you will need to install the UCMA redistributable, available as part of the UCMA SDK download:
http://www.microsoft.com/downloads/details.aspx?FamilyID=b30306e4-7dc1-44d2-9a68-9b4a6fc5c2df&displaylang=en

You will also need to modify, compile and add the AlertSender.OCS_Sender .Net reference to AX, which is available to download here:
http://www.microsoft.com/downloads/details.aspx?FamilyId=9EFC784B-E443-4441-926C-5FD405D41BD9&displaylang=en

Written by gregondax

January 18, 2010 at 9:02 am

Solving an ‘Object object has not been initialized’ runtime error

leave a comment »

If you have received the error below, and are scratching your head over the cause:

Object object not initialised error

Then the answer is to check you have called super() in your constructor. The compilation output will also display a warning to that effect:

Compile warning for not overriding super in constructor

Related notes about extending Object and initialization

Unlike C#, Object is not implicitly extended when creating a new class. You’ll need to ensure that the ‘extends object’ class declaration is included if you want to call methods declared in Object, eg:

extendsObject

Repeating the aforementioned error (and also unlike C#) the base constructor is not implicitly called in X++:

void new()
{
;
super(); //base constructor is not called implicitly
}

Written by gregondax

November 23, 2009 at 9:30 am

AIF Pipeline Example

with 4 comments

In my last post (Top 5 AIF Development Tips and Tricks) I mentioned how you can use the AIF to transform external messages into the format that AX expects. This post elaborates upon that tip with a more detailed example.

Suppose you want to import currency exchange rates.

AX has an action for this out of the box – createListExchangeRates, and will happily accept a message similar to this:

<ExchangeRates xmlns="http://schemas.microsoft.com/dynamics/2006/02/documents/ExchangeRates">
    <DocPurpose>Original>/DocPurpose>
    <ExchRates>
        <CurrencyCode>GBP</CurrencyCode>
        <ExchRate>1.71</ExchRate>
        <FromDate>2009-09-21<FromDate>
        <ToDate>2009-09-22</ToDate>
    </ExchRates>
</ExchangeRates>

Thats all well and good if you have control over the schemas of any inbound messages.
Most likely this is not the case, say for example you want to import the exchange rates for the euro from the european central banks daily published list (http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml).

If you try and receive this it will fail on two counts:
1) It is not wrapped in an envelope (which is used by the AIF to identify the message sender and destiation) and
2) the format of the exchange rate does not match AX schema.

To solve problem 1) you can make use of the ‘Wrap XML in AIF envlope’ program available from axaptapedia here:www.axaptapedia.com/AIFEnvelopeTool

To solve 2) you can use the pipeline to specify an XSLT to transform the message, the XSLT to perform the transformation for this example is:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:ax="http://schemas.microsoft.com/dynamics/2006/02/documents/ExchangeRates" 
  xmlns:ext="http://www.ecb.int/vocabulary/2002-08-01/eurofxref" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" 
  exclude-result-prefixes="ext xs xsi xsl gesmes" 
  xmlns="http://schemas.microsoft.com/dynamics/2006/02/documents/ExchangeRates">    
  <xsl:namespace-alias stylesheet-prefix="ax" result-prefix="#default" />
    <xsl:output method="xml" encoding="UTF-8" indent="yes" />
    <xsl:template match="gesmes:Envelope">
      <ax:ExchangeRates>          
          <xsl:variable name="root" select="." />          
          <xsl:for-each select="$root/ext:Cube/ext:Cube">
            <xsl:variable name="xrateDate" select="$root/ext:Cube/ext:Cube/@time" />
              <xsl:for-each select="$root/ext:Cube/ext:Cube/ext:Cube">
              <ax:ExchRates>
                <xsl:attribute name="class">
                  <xsl:value-of select="'entity'" />
                </xsl:attribute>
                <ax:CurrencyCode>
                  <xsl:value-of select="@currency" />
                </ax:CurrencyCode>
                <ax:ExchRate>
                  <xsl:value-of select="@rate" />
                </ax:ExchRate>
                <ax:FromDate>
                  <xsl:value-of select="$xrateDate" />
                </ax:FromDate>
                <ax:ToDate>
                  <xsl:value-of select="$xrateDate" />
                </ax:ToDate>
              </ax:ExchRates>
            </xsl:for-each>
          </xsl:for-each>        
      </ax:ExchangeRates>         
    </xsl:template>  
</xsl:stylesheet>

After running the AIF inbound service upon last weeks exchange rates it results in the following data being created (US Dollar highlighted):

Exchange rates AX form

Exchange rates AX form

Written by gregondax

October 26, 2009 at 8:00 am

Top 5 AIF Development Tips and Tricks

with 2 comments

1. Enable the debugger

You will notice, if you put a breakpoint into your document class, that when you run the associated action, the breakpoint will get ignored – leaving you in the dark. This is because the action is called using a ‘runas’ function call  to change the user executing the code.

To workaround this replace the runas method calls with direct calls in\Classes\AifOutboundProcessingService\ and \Classes\AifInboundProcessingService, eg:

/*
runas(message.sourceEndpointAxUserId(),
classnum(AifInboundProcessingService),
staticmethodstr(AifInboundProcessingService, processAsUser),
[message.pack(), messageId]);
*/
AifInboundProcessingService::processAsUser([message.pack(), messageId]);

2. Run jobs instead of waiting for batch processing

The usual way for the AIF to run is using the batch processing framework, where you setup an interval for the inbound and outbound processing  to run.  This minute or so can feel like an age when you are in the middle of developing:

AIF batch processing

AIF batch processing

So use a custom job to have the AIF run instantly at the click of a button, here is an example of the receive job:


static void runAIFReceive(Args _args)
{
AifGatewayReceiveService aifGatewayReceiveService;
AifInboundProcessingService aifInboundProcessingService;
;

aifGatewayReceiveService = new AifGatewayReceiveService();
aifGatewayReceiveService.run();
aifInboundProcessingService = new AifInboundProcessingService();
aifInboundProcessingService.run(true);  // pass true for debug mode
}

3. Use file adapters

fileAdapter

When using the AIF, you will most likely be using a Biztalk, Web service or MSMQ adapter. Testing actions using one of these adapters can be a pain as you will most likely require another program to send or receive the message.

To get around this you can use a file adapter during testing, so that you can just write the message in plain XML and drop the file into a directory on your file system to be processed.

Then when you are finished testing / developing you can easily swap the file adapter out.

4. Compose messages with the Visual Studio XML editor

During development (when you use a file adapter), you can create the message using notepad or any other text editor. I recommend using the Visual Studio XML editor to quickly compose these to take advantage of intellisense, schema validation and other useful features (like inserting a guid):

xmlEditVS2008

5. Use the pipeline

It is unlikely (if you are integrating with a third party) that the XML schemas  of the external system match those in AX.

To transform the message into the format AX can handle you can use the pipeline to add a component to run an xslt on the inbound XML:

pipelineConfig

You can also create custom pipeline components, see this link for more information –

How Do I: Create a Custom AIF Pipeline Component?

Written by gregondax

September 24, 2009 at 1:23 pm

X++ and C# Compared

leave a comment »

With the advent of Dynamics AX 2009, the need for AX developers to know .Net is increasing.

I have noticed the AX developer documentation on MSDN getting better and better, here is a section comparing X++ and C# that is well worth a read, check it out!:

X++ and C# Comparisons

Dynamics AX MSDN Developer Center – X++ and C# comparisons

Written by gregondax

June 12, 2009 at 9:00 am