« »

search

Art Of Creation – Dynamics AX Blog

 The everyday life of a Dynamics AX developer

  • AX2012: SysOperation part 2: SysOperationServiceController

    August 23, 2011 at 12:20  (18 comments)

    Hi again.

    In part 1, we created a nice batch using data contracts, service operation and the SysOperationServiceController class.

    Today, we will place our menu item on the Customers form, so we can process the customer that is selected in the grid. It will look like this:

    A thing that is still missing though, is a way to work with the Args that are passed when a menu item is executed. We want to be able to set the query on our data contract before showing the batch dialog. We might also want to have a default value in some fields, for example, the date should be today’s date every time we launch the dialog.
    We can do all of that by extending the SysOperationServiceController class.

    So let’s do that!

    Create a new class that extends SysOperationServiceController

    class KlForCustTesterServiceController extends SysOperationServiceController
    {
    }

    Now let’s create a method that constructs an instance of this class based on the Args object

    public static KlForCustTesterServiceController newFromArgs(Args _args)
    {
        KlForCustTesterServiceController    klForCustTesterServiceController;
        ;

        // create a new instance of the controller
        klForCustTesterServiceController = new KlForCustTesterServiceController();
        // initialize from args
        // one of the things this will do is read the "parameters" property from the menu item
        klForCustTesterServiceController.initializeFromArgs(_args);

        // return a new instance of this controller
        return klForCustTesterServiceController;
    }

    Above is the basic code that should be in your construct method. However, this doesn’t do anything more than the SysOperationServiceController already does, so let’s add the logic that sets the date to today’s date.

    public static KlForCustTesterServiceController newFromArgs(Args _args)
    {
        KlForCustTesterServiceController    klForCustTesterServiceController;
        klForCustTesterDataContract     klForCustTesterDataContract
        ;

        // create a new instance of the controller
        klForCustTesterServiceController = new KlForCustTesterServiceController();
        // initialize from args
        // one of the things this will do is read the "parameters" property from the menu item
        klForCustTesterServiceController.initializeFromArgs(_args);
       
        // get datacontract
        // the string should be the same as the parameter name!
        klForCustTesterDataContract = klForCustTesterServiceController.getDataContractObject('_klForCustTesterDataContract');

        // default current date
        klForCustTesterDataContract.parmTransDate(systemDateGet());

        // return a new instance of this controller
        return klForCustTesterServiceController;
    }

    As you can see, the Data Contract is fetched using the getDataContractObject() method. This will also unpack any values that were packed before, just like a normal RunBase would.
    Setting the date is a simple as setting the parm method on the data contract.

    Now let’s add the logic that creates the correct query by adding a range based on the record in the Args variable.

    public static KlForCustTesterServiceController newFromArgs(Args _args)
    {
        KlForCustTesterServiceController    klForCustTesterServiceController;
        klForCustTesterDataContract         klForCustTesterDataContract;
       
        CustTable   custTable;
        Query       query;
        ;

        // create a new instance of the controller
        klForCustTesterServiceController = new KlForCustTesterServiceController();
        // initialize from args
        // one of the things this will do is read the "parameters" property from the menu item
        klForCustTesterServiceController.initializeFromArgs(_args);
       
        // get datacontract
        // the string should be the same as the parameter name!
        klForCustTesterDataContract = klForCustTesterServiceController.getDataContractObject('_klForCustTesterDataContract');

        // default current date
        klForCustTesterDataContract.parmTransDate(systemDateGet());
       
        // check if the record is of type CustTable
        if(_args && _args.dataset() == tableNum(CustTable))
        {
            // cast record
            custTable = _args.record();

            // create new query
            query = new query(queryStr(KlForCustomers));
            // add range
            query.dataSourceTable(tableNum(CustTable)).addRange(fieldNum(CustTable, AccountNum)).value(queryValue(custTable.AccountNum));

            // set query on datacontract
            klForCustTesterDataContract.setQuery(query);

        }

        // return a new instance of this controller
        return klForCustTesterServiceController;
    }

    There you go. Now we need to make a main method that uses this construct method, and starts the operation:

    public static void main(Args _args)
    {
        KlForCustTesterServiceController klForCustTesterServiceController;
        ;

        klForCustTesterServiceController = KlForCustTesterServiceController::newFromArgs(_args);
        klForCustTesterServiceController.startOperation();
    }

    We also need to create a new menu item. Right click you project – New – Menu Item.
    Enter the following properties:

    ObjectType: Class
    Object: KlForCustTesterServiceController
    Parameters: KlForCustTesterDataService.testCustomer

    Next, add the menu item to the CustTable form:

    Finally before testing, click the Generate Incremental CIL button to generate CIL.

    Right click the CustTable form and click Open. This will open the Customers form. Switch to the grid view (Ctrl + Shift + G), and select a record. Then click the button we just added.

    Note: a bug (or something I do wrong?) I found is that the query values that are displayed on the dialog seem to be “lagging”. The wrong values are shown, but when you click ok, the correct values are used, unless you click the select button.
    This can be fixed by adding this line before returning the controller object:

    KlForCustTesterServiceController.queryChanged("_klForCustTesterDataContract.parmQuery", query);

    This is probably not a nice way to do this, so I’ll look into it, and let you know.

    When you click Ok on the dialog, you should see that only the customer you’ve selected will be processed. You will also see that the date has been set to today’s date, just like we wanted.

    Download the XPO for part 2 here.

  • AX2012: SysOperation part 1: Data Contracts and Service Operations

    August 22, 2011 at 12:00  (17 comments)

    Hi All :-)

    This is part 1 of a series of 3 blog posts where I demonstrate how to create batch classes using the SysOperation classes (Read the introduction here)

    We are going to create a batch that looks like this:

    And we are going to do that without creating a RunBaseBatch class!

    Note: Just to be clear: the batch class won’t actually do anything useful, everything is just for demo purposes.

    Okay, let’s start!

    1. Data Contract

    First, we will create a data contract. The data contract contains data members that represent the fields that will be available on the dialog.

    Let’s create a new class, KlForCustTesterDataContract:

    [DataContractAttribute]
    class KlForCustTesterDataContract
    {
    }

    The string DataContractAttribute indicates that this class is a Data Contract. The square brackets [] indicate that this string is an attribute (You can read more about attributes on MSDN).

    Next, let’s add Data Members to this contract by first declaring them in the class declaration

    [DataContractAttribute]
    class KlForCustTesterDataContract
    {
        Name        name;
        TransDate   transDate;
        str         packedQuery;
    }

    We add a string field, a date field, and a query. We still need to create parm methods for these variables.

    This is just a simple string field, nothing special here except for the DataMemberAttribute attribute that indicates that this method is da data member.

    [DataMemberAttribute]
    public Name parmName(Name _name = name)
    {
        name = _name;
        return name;
    }

    The date field is of type TransDate, so we’ll give it a nicer label by using the SysOperationLabelAttribute attribute to specify the label used.
    Also note that, when two attributes are specified, the are separated by a comma (,).

    [DataMemberAttribute
    ,SysOperationLabelAttribute(literalStr("@SYS11284"))] // today's date
    public TransDate parmTransDate(TransDate _transDate = transDate)
    {
        transDate = _transDate;

        return transDate;
    }

    We will also add a query, so we can loop all customers. As you can see, we can specify the query by using the AifQueryTypeAttribute. The query KlForCustomers is just a query in the AOT with CustTable as a datasource (see xpo file at the end).

    [DataMemberAttribute,
        AifQueryTypeAttribute('_packedQuery', querystr(KlForCustomers))
    ]
    public str parmQuery(str _packedQuery = packedQuery)
    {
        packedQuery = _packedQuery;
        return packedQuery;
    }

    Next, we add two helper methods that are not Data Members, so we can easily set and get the query variable.

    To get the query:

    public Query getQuery()
    {
        return new Query(SysOperationHelper::base64Decode(packedQuery));
    }

    To set the query:

    public void setQuery(Query _query)
    {
        packedQuery = SysOperationHelper::base64Encode(_query.pack());
    }

    2. Service class and operation

    Next, let’s create a service class called KlForCustTesterDataService.

    class KlForCustTesterDataService
    {
    }

    Right click the class, click properties, and set the run on property to server.

    Adding a service operation to this service class is as simple as adding a method. As you can see, the SysEntryPointAttribute attribute indicates that this is a service operation, and our data contract is used as an argument.

    [SysEntryPointAttribute]
    public void testCustomer(KlForCustomerTesterDataContract _klForCustomerTesterDataContract)
    {
    }

    Of course, this method needs some demo functionality, so let’s add that:

    [SysEntryPointAttribute]
    public void testCustomer(KlForCustTesterDataContract _klForCustTesterDataContract)
    {
        QueryRun    queryRun;
        CustTable   custTable;
        ;
       
        // info the name parameter
        info(_klForCustTesterDataContract.parmName());

        // create a new queryrun object
        queryRun = new queryRun(_klForCustTesterDataContract.getQuery());

        // loop all results from the query
        while(queryRun.next())
        {
            custTable = queryRun.get(tableNum(custTable));

            // display the accountnum
            info(custTable.AccountNum);
        }
    }

    3. Service

    Right click your project, and choose New – Service. Give this service the same name as the service class, KlForCustTesterDataService.
    In the properties of this node, set the class property to KlForCustTesterDataService
    Expand the node of your service, then right click on the Operations node.
    Click Add operation and check the add checkbox next to the method we’ve created, and click OK.

    4. Menu item

    All we need to do now is create a menu item so we can start our dialog.
    Do this by right clicking your project – New – Menu item. Name it KlForCustTesterDataService.

    Enter the following properties:
    ObjectType: Class
    Object: SysOperationServiceController
    Parameters: KlForCustTesterDataService.testCustomer

    As you can see, we don’t link directly to our service class, in stead, the class SysOperationServiceController will be used, and will know what service operation to start because it is specified in the parameters property. More about these controller class in part 2 of this series.

    5. Run it!

    But wait, we still have to do one small thing: compile our X++ into the common intermediate language (CIL). Don’t worry, it’s easy. Just click the Generate Incremental CIL button in the toolbar (this might take a while).

    After CIL was generated, right click the menu item and click Open. You should see your batch class. As you can see, all fields from the datacontract are displayed, and a select button was automatically generated so you can modify the query.

    Output when run in batch:

    In part 2, we will take a closer look at the SysOperationServiceController class. See you tomorrow.

    Download the XPO for part 1 here.

  • AX2012: SysOperation introduction

    August 21, 2011 at 17:28  (6 comments)

    Hi everyone!

    Dynamics AX 2012 introduces a new way for creating batches, without using the RunBaseBatch class. The SysOperation* classes are part of the Business Operation Framework, and can be used to create these batches.

    Some documentation is already available on MSDN about the BOF and the SysOpertation classes, but I was still confused when creating such a batch class. That is why in the next 3 days, I will cover the basics on how to create these batches, so you don’t have to figure it all out on your own.

    I hope to get some feedback too, because not everything is crystal clear to me either :-).

    But anyway, this is what you can expect in the next 3 days:

    Day 1 (link)
    A demonstration on how to create Data Contract classes and Service Operations. You will also learn how to use attributes like DataContractAttribute and DataMemberAttribute, and at the end, we will already have a working batch class.

    Day 2 (link)
    We will take a closer look at the SysOperationServiceController class, and will be creating our own controller. This will allow us to set properties on our Data Contract based on Args. This is useful when you batch is started by a button on a form that is linked to a datasource.

    Day 3 (link)
    To make a fully functional batch, we need to be able to overwrite methods on our dialog field like lookup and modified. We also want to be able to change the properties of these fields, like enabling or disabling them. That’s just what the SysOperationAutomaticUIBuilder class is for, and we’ll look into that.

  • Top reasons to follow best practices

    August 16, 2011 at 18:14  (no comments)

    Hi all.

    Here’s a list of af few reasons why I think it is important to fix those best practice errors :-).

    Improved performance
    Many best practices have an influence on performance. For example, it is best practice to set the property CreateRecIdIndex to True on tables with Created/Modified DateTime fields. If you don’t, this will have an adverse effect on the performance of those tables.
    Another example is that if you adhere to the best practices concerning the construction of classes and the use of batch classes, it will be much easier to improve performance by enabling batch multi-threading.

    Improved readability of code
    When all developers follow best practices about formatting of code, the code will be more readable and easier to understand. Avoiding dead code and removing unused variables will also make the code less confusing.

    Developers learn
    There are a lot of handy things you can learn by solving best practice deviations. Adding fields to field groups for example is a great way to avoid customizations to forms. And by figuring out what configuration is needed on a menu item, you learn about what effect configuration keys have on you solution.

    Improved security
    Adding security and configuration keys is vital to good security. When ignoring best practices, an unauthorized user might cause a big mess that you’ll have to clean up.

    Certified for Dynamics AX
    Making sure your solution is best practice deviation free goes a long way towards passing the Software Solution Test.

    Better user experience
    Some best practices have to do with labels, help texts, descriptions for batch task, etc. If you ignore those, it will be more difficult for the user to understand the solution you created.

  • Try Catch and transactions

    August 10, 2011 at 11:57  (3 comments)

    Hi all,

    Exception handling can be quite confusing in Dynamics AX, so I wanted to share some quick facts about try/catch and transactions.

    The general rule is that exceptions are caught in the outer most catch, where the ttslevel is 0. This means that if you put a transaction around a try/catch, your exceptions will not be caught.
    The following two jobs demonstrate that:

    Transaction inside try/catch:

        try
        {
            ttsBegin;
            throw error("an error");
            ttsCommit;
        }
        catch
        {  
            info("error caught");
        }

    Output:

    Error an error
    Info error caught

    Try/catch inside transaction:

        ttsBegin;
        try
        {
            throw error("an error");  
        }
        catch
        {  
            info("error caught");
        }
        ttsCommit;

    Output:

    Error an error

    As you can see, the error is not caught when the transaction is around the try catch.

    However, there are two exceptions to this rule.
    The following code demonstrates that UpdateConflict and DupplicateKeyException can be caught inside a transaction.

        Set             set = new Set(Types::Enum); // a set containing all possible exceptions
        SetEnumerator   se;                         // enumerator used to loop though the set with exception
        Exception       exception;                  // used to cast the value from the set
        boolean         caughtInside;
        ;
       
        // add all exception to a set
        set.add(Exception::Break);
        set.add(Exception::CLRError);
        set.add(Exception::CodeAccessSecurity);
        set.add(Exception::DDEerror);
        set.add(Exception::Deadlock);
        set.add(Exception::DuplicateKeyException);
        set.add(Exception::DuplicateKeyExceptionNotRecovered);
        set.add(Exception::Error);
        set.add(Exception::Info);
        set.add(Exception::Internal);
        set.add(Exception::Numeric);
        set.add(Exception::PassClrObjectAcrossTiers);
        set.add(Exception::Sequence);
        set.add(Exception::Timeout);
        set.add(Exception::UpdateConflict);
        set.add(Exception::UpdateConflictNotRecovered);
        set.add(Exception::Warning);
       
        // create enumerator
        se = set.getEnumerator();
       
        // loop all exceptions
        while(se.moveNext())
        {
            // set flag false
            caughtInside = false;
            // begin outer try catch
            try
            {
                ttsBegin;
                // begin inner try catch
                try
                {
                    // cast exception
                    exception = se.current();
                    // trhow exception
                    throw exception;
                }
                catch
                {
                    // set flag to indicate the exception was caught inside the transaction
                    caughtInside = true;
                   
                    warning(strFmt("%1 can be caught inside transaction", exception));
                    // throw exception again to catch it outside of transaction
                    throw exception;
                }
                ttsCommit;
            }
            catch
            {
                // for once, it's ok to catch everyting :)  
               
                if(caughtInside)
                {
                    warning(strFmt("%1 can also be caught outside of the transaction", exception));
                }
                else
                {
                    info(strFmt("%1 can only be caught outside of the transaction", exception));
                }
               
            }
        }

    Output:

    Info Info can only be caught outside of the transaction
    Info Warning can only be caught outside of the transaction
    Info Deadlock can only be caught outside of the transaction
    Info Error can only be caught outside of the transaction
    Info Internal can only be caught outside of the transaction
    Info Break can only be caught outside of the transaction
    Info DDEerror can only be caught outside of the transaction
    Info Sequence can only be caught outside of the transaction
    Info Numeric can only be caught outside of the transaction
    Info CLRError can only be caught outside of the transaction
    Info CodeAccessSecurity can only be caught outside of the transaction
    Warning UpdateConflict can be caught inside transaction
    Warning UpdateConflict can also be caught outside of the transaction

    Info UpdateConflictNotRecovered can only be caught outside of the transaction
    Warning DuplicateKeyException can be caught inside transaction
    Warning DuplicateKeyException can also be caught outside of the transaction

    Info DuplicateKeyExceptionNotRecovered can only be caught outside of the transaction
    Info Timeout can only be caught outside of the transaction
    Info PassClrObjectAcrossTiers can only be caught outside of the transaction

    It is also noteworthy that, when you enter a catch block, there has been a implicit ttsabort, so the transaction has been rolled back. However this is not true for UpdateConflict and DuplicateKeyException when they were caught inside a transaction.

  • Try Catch example code

    August 9, 2011 at 19:04  (11 comments)

    Hi all!

    The code below contains a try/catch that I use a lot when developing batch jobs, especially multithreaded ones.
    It deals with frequently occurring exceptions that, in some cases, can be easily solved by retrying:

    • Deadlocks
    • Update conflicts
    • Duplicate key conflicts

    Duplicate key conflicts are more rare than update conflicts, but I’ve seen them pop up under heavy load on InventDim records.

        #OCCRetryCount
       
        try
        {
            ttsbegin;

            // do stuff here
           
            ttsCommit;
        }
        catch (Exception::Deadlock)
        {
            // retry on deadlock
            retry;
        }
        catch (Exception::UpdateConflict)
        {
            // try to resolve update conflict
            if (appl.ttsLevel() == 0)
            {
                if (xSession::currentRetryCount() >= #RetryNum)
                {
                    throw Exception::UpdateConflictNotRecovered;
                }
                else
                {
                    retry;
                }
            }
            else
            {
                throw Exception::UpdateConflict;
            }
        }
        catch(Exception::DuplicateKeyException)
        {
            // retry in case of an duplicate key conflict
            if (appl.ttsLevel() == 0)
            {
                if (xSession::currentRetryCount() >= #RetryNum)
                {
                    throw Exception::DuplicateKeyExceptionNotRecovered;
                }
                else
                {
                    retry;
                }
            }
            else
            {
                throw Exception::DuplicateKeyException;
            }
        }
  • AIF: There is no service with namespace = ‘http://yournamespace’ and external name = ‘aService’.

    April 27, 2011 at 18:57  (1 comment)

    Hi all :-)

    Today, when testing newly deployed AIF web services, this error was thrown:

    There is no service with namespace = ‘http://yournamespace’ and external name = ‘aService’.

    Where ‘http://yournamespace’ is the namespace you specified on the service node in the AOT, and ‘aService’ is the external name of your service.

    The problem is that on the service node of this service in the AOT, there was a ‘/’ (slash) at the end, like this:
    service with slash
    Removing the slash at the end fixes the problem:
    service without slash

    I did a refresh in the Service form and redeployed my services on the IIS, so you might have to do that too.

  • SQLDictionary synchronization problem

    March 22, 2011 at 10:47  (no comments)

    Today, the following message popped up when synchronizing the Data Dictonary (Dutch):

    Kan meerdere records in Table list (SqlDictionary) niet toevoegen. Tabel: 118, 4.
    De SQL-database heeft een fout gegenereerd:

    It roughly translates to:

    Cannot insert multiple records in Table list (SQLDictionary). Tabel 118, 4. The SQL-Database has issued an error:

    Thanks to my colleague Kenny, we quickly figured out that the problem was that the disk was full on the SQL server where the data and log files were stored. SQL server also logged an error in the event viewer on the SQL server saying that the disk was full.

    Lessons learned:

    • Errors in AX don’t always point in the right direction
    • Check the event viewer on your servers
    • Be carefull with low disk space
  • X++ Performance tips

    February 6, 2011 at 14:21  (5 comments)

    Hi everyone.

    I want to do an other post about performance. But not so much about exotic SQL stuff or hidden settings, but simple X++ performance tips.
    These are the first things I do and look for when there is a performance problem, because they are easy and obvious things to fix.

    Tip 1: Measure execution time of your code
    Measuring is knowing. Before you start changing code, make sure you have a set of data you can keep reusing for your tests. Measure the performance of your code on that data after each change in code so you know the impact of your changes.

    One way to do this is by using the Winapi::getTickCount() (or WinApiServer::getTickCount() if your code runs on server) method.

    static void KlForTickCountSample(Args _args)
    {
        int ticks;
        ;

        // get the tickcount before the process starts
        ticks = winapi::getTickCount();
       
        // start the process
        sleep(2000); // simulate 2 seconds of processing
       
        // compare tickcount
        ticks = winapi::getTickCount() - ticks;
       
        // display result
        info(strfmt('Number of ticks: %1', ticks));
    }

    Tip 2: limit the number of loops
    A LOT of time goes into loops. If you have a performance problem, start looking for loops. Code can run really fast, but it can get slow when it is executed too many time, eg, in a loop.

    Tip 3: avoid ‘if’ in ‘while select’
    When there is a ‘if’ in a ‘while select’, see if you can rewrite it a a where statement in your select. Don’t be affraid use a join either. Consider the following example:

    static void KlForIfInLoop(Args _args)
    {
        VendTable vendTable;
        ;

        // usually slower
        while select vendTable
        {
            if(vendTable.VendGroup == 'VG1')
            {
                info(vendTable.AccountNum);
            }
        }

        // usually faster
        while select vendTable
        where vendTable.VendGroup == 'VG1'
        {
            info(vendTable.AccountNum);
        }
    }

    Tip 4: avoid double use of table methods
    Using table methods a lot can get really slow if you do it wrong.

    Consider the following example:

    static void klForTableMethodsSlow(Args _args)
    {
        SalesLine salesLine;
        InventDim inventDim;
        ;
       
        // select a salesline
        select firstonly salesLine;
       
        inventDim.InventColorId = salesLine.inventDim().InventColorId;
        inventDim.InventSizeId  = salesLine.inventDim().InventSizeId;
        inventDim.inventBatchId = salesLine.inventDim().inventBatchId;
    }

    This example code looks nice, but there’s a problem.
    The salesLine.inventDim() method contains the following:

    InventDim inventDim(boolean  _forUpdate = false)
    {
        return InventDim::find(this.InventDimId, _forUpdate);
    }

    This means that the invendDim record is read three times from the database.
    It is better to declare the inventDim record locally and only retrieve it once:

    static void klForTableMethodsFast(Args _args)
    {
        SalesLine salesLine;
        InventDim inventDim;
        InventDim inventDimLoc;
        ;
       
        // select a salesline
        select firstonly salesLine;
       
        inventDimLoc = salesLine.inventDim();
       
        inventDim.InventColorId = inventDimLoc.InventColorId;
        inventDim.InventSizeId  = inventDimLoc.InventSizeId;
        inventDim.inventBatchId = inventDimLoc.inventBatchId;
    }

    Tip 5: Don’t put too much code on tables
    Code on tables is usually fast, but things can get slow if you use it to much.
    Say you have a table with an InventDimId field. If you have 5 methods that need the InventDim record, because you don’t have a classDeclaration method on your table, you need to call this function 5 times, once in every method:

    InventDim::find(this.inventDim)

    .
    When you put these methods on a class, you could optimise it by fetching the record only once and storing it in the classDeclaration, or better, passing it as a parameter to your methods.

    An other example is fetching parameters from parameter tables, eg InventParameters::find(). On a table, you have to fetch it each time you call a method. In a class, you would probably optimize your code to only fetch the parameter record once.

    Tip 6: Use the fastest code
    For some tasks, there is ‘special code’ that is faster than the code you would normally write.
    For example:

    // slower
    while select forupdate custTable
    where custTable.custGroup == 'TST'
    {
        custTable.delete();
    }

    // faster
    delete_from custTable
    where custTable.custGroup == 'TST';

    The same applies to update_recordset for updating records.

    Also, when adding values to the end of a container

    cont += "a value";

    is faster than

    cont = conins(cont, conlen(cont), "a value");

    Tip 6: Every optimization counts
    Remember that every optimization you do to you code counts, even if it’s a little one. Small performance tweaks can have a huge effect once you process large quantities of data. So don’t be lazy, and optimize :-).

  • News from the Dynamics AX Technical Conference 2011

    January 20, 2011 at 11:29  (4 comments)

    Many of you are at the technical conference right now, but if you’re not (like me unfortunately), you’re probably readling blogs to see what’s new in AX2012 (aka AX6).

    Two of my colleagues are there, and they are keeping up to their promise of blogging about the interesting stuff that is being unveiled (thanks guys!).

    Check out their blogs:
    – Tom Van Dyck
    – Kenny Saelen (or follow on twitter)

    Also check the #DAXCONF tag on twitter for tweets.

    Warning: it could be overwhelming ;-)

  • Happy 2011

    January 1, 2011 at 00:00  (no comments)

    Happy new year to everyone!
    I wish you all a lots of successful Dynamics AX implementations in 2011 ;-).

    happy 2011
    (Image © jscreationzs)

  • Decisions Fall 2010 session content available next week

    November 2, 2010 at 11:03  (1 comment)

    For everyone that missed (part of) the AX day of Decisions Fall 2010 yesterday, the sessions will be available on demand somewhere next week.

    Keep an eye on their website.

  • Tomorrow Decisions Virtual Conference – AX Day

    October 31, 2010 at 19:35  (no comments)

    Tomorrow the 1st of November is the start of Decisions Fall 2010, a 4 day, free online virtual conference about AX, GP, NAV and CRM.

    Check out what sessions are available for AX here: Dynamics AX program
    You can still register (for free) for this event, so what are you waiting for :-).

    Thank to Kenny for letting me know about this event.

  • Using the UserConnection class to create a new transaction scope

    October 20, 2010 at 18:30  (2 comments)

    Hi All!

    Here’s a trick I learned from a colleague:
    With the class UserConnection, you can make sure a transaction isn’t rolled back even if the transaction it is in is rolled back.

    Consider the following example:

    static void KlForRecordNotInserted(Args _args)
    {
        CustTable custTable;
        ;
       
        ttsbegin;
       
        custTable.clear();
        custTable.initValue();
        custTable.AccountNum = "000010";
        custTable.Name = "My Test Customer";
        custTable.insert();
       
        throw error("An error that causes a rollback");
        ttscommit;
    }

    The record will not be inserted, because an error is thrown that causes an implicit ttsabort (so the tranaction is rolled back), right?

    Well, you can counter this behavior by using the UserConnection class:

    static void KlForRecordInserted(Args _args)
    {
        UserConnection userConnection;
        CustTable custTable;
        ;
       
        ttsbegin;
       
        userConnection = new userConnection();
       
        custTable.clear();
        custTable.initValue();
        custTable.AccountNum = "000020";
        custTable.Name = "My Test Customer";
        custTable.setConnection(userConnection); // set userconnection
        custTable.insert();
       
        throw error("An error that causes a rollback");
        ttscommit;
    }

    You will see that the CustTable record will be inserted in the example above.

    This can be useful if you really want a record to be inserted, for example when doing logging (like in batch processes, or when debugging).
    When you don’t use a UserConnection, your logging will be rolled back together with other transaction.

  • Dynamics AX Performance Optimization

    October 17, 2010 at 22:30  (3 comments)

    The Belgian Dynamics Community organized a ‘Connection Day’ about Performance Optimization on the 1st of October.
    They have put the session content online here .

    If you are looking for ways to solve performance issues, be sure to check it out.

    Also, I found this nice PowerPoint presentation titled ‘AX Troubleshooting Tips’ while searching for ways to trace long running queries.
    Download it here.

  • « Previous Page

    Next Page »

     

Best value on tickets: wickedthemusicaltickets.top 100% guaranteed tickets: luke bryan concert tickets Discount Broadway Tickets - hamiltonmusicaltickets.top View dates for upcoming Billy Joel concert tour - http://billyjoeltourtickets.top Discount Tickets for Broadway, find cheap Hamilton Broadway Tickets Tickets On Sale For All Dates - Eric Church Tour 2017 Major League Baseball Tickets. Get your SF Giants Tickets and save Search all sellers and save on cheap Indians Tickets

Wordpress // Photon theme // Copyright © Klaas Deforche

 

Loading Comments...