mfp's two cents

...on Dynamics AX Development!
  • mfp's two cents

    Late night discussion on software development #1: Continuous publishing

    • 3 Comments

    “So tell me”, I start, “when was the last time you used a help system?”

    Søren pours himself another glass of red wine and leans back in his armchair. He is silent for a while. “What do you mean when you say help system?”

    I open my mouth, and close it again. It always amazes me, when I realize the depth of such a seemingly naïve question. My immediate response seems too narrow. I have to break out of my standard way of thinking. A help system is not just a part of a larger system that helps you when you are stuck. It is even more than this. What comes out of my mouth is: “A help system is a system that helps you accomplish your tasks.”

    “In that case I use help systems daily”, Søren proclaims.

    “Really? I thought you already knew all the answers.”, I cannot resist the temptation.

    Søren smiles, “You are also a developer, how do you manage to get anything done without a help system?”

    Apparently Søren is into one of his moods where he answers all my questions with a new question. I sip my wine, take a deep breath and start to elaborate. “As a developer my work is quite dependent of the phase my project is in. Let's look at my favorite, the implementation phase. While writing code I never press the F1 button. I have worked with my current code editor for so long, that I’m fully aware of what it can and cannot do for me.”

    Søren says: “So you have turned off IntelliSense?”

    “No way – I don’t even know any developer crazy enough to do that. Actually I don’t even know an code editor that allows you to turn off IntelliSense.”

    “Please go back to your definition of a help system.”

    It dawns on me. Help system are more than just F1 help. It is anything that enables me to do my job. “Ok”, I say, “So what other help systems do you use?” I ask. I’m starting to feel confident I will earn more than a hangover from tonight.

    “Well,” Søren says, “as a developer I often need to lookup API reference information. To do that I use the web constantly.”

    “So you actually find any useful information out there? I’ve tried a few times but given up. Instead I browse objects locally searching for something that will satisfy my need.”

    “That is one of the industry’s dilemmas. Users who have been disappointed by the documentation available in the help system, usually never come back. So why bother writing documentation, if it is not going to be read?“

    “Exactly, if the documentation isn’t there, then there is no point in looking.”

    Søren continues, “The users also suffers here, instead of looking for information where it is obvious they eagerly jump into the haystack looking for the needle. How can this destructive habit be reversed?”

    A simple question, I think to myself. I answer: “Well, if the software providers just wrote the documentation we need, we wouldn’t have the problem.”

    Søren promptly replies: “The question to ask is: How do you know it is not already there?” I feel like I’m being maneuvered into a corner. “When I installed my environment about 2 years ago, I browsed through the reference documentation, and it was primarily empty placeholders.”,  I pause to think about what I just said. “Naturally these placeholders might have been populated in the meantime.“ I make a mental note to  reserve some time on Monday morning to give the online documentation a second chance.

    Søren finishes off his glass of red wine. He looks content.

  • mfp's two cents

    Version control for everyone

    • 3 Comments
    With Dynamics AX 5.0 there is no longer an excuse for not using a source control system when developing in MorphX. The version control integration options in Dynamics AX 5.0 both cater to larger development organizations by seamlessly integrating with Visual Source Safe and Team Foundation Server, and to smaller development teams who cannot afford the additional overhead of these larger system with a new, simple, yet powerful, version control system: MorphX VCS.

    All 3 flavours enable check-in/check-out/undo check-out/change history/change descriptions/quality bar enforcements etc. Using a version control system can dramatically improve the quality, predictability and productivity of your MorphX projects.

    This channel 9 screencast gives a preview of the version control system integration options in the next release of MorphX - the IDE of Dynamics AX. It shows a side-by-side comparison of the integration options with Team Foundation Server, Visual Source Safe, and MorphX VCS. The last half of the screencast gives a demonstration of MorphX VCS.

    Finally I want to wish everyone a "God Jul" (as you say this time of year where I live.)

  • mfp's two cents

    Now available: Dynamics AX 2009 Pre-Release (CTP3) Demonstration Toolkit

    • 3 Comments

    We are happy to announce that the new Microsoft Dynamics AX 2009 Pre-Release (CTP3) Demonstration Toolkit is now available on Partner Source.

    The Dynamics AX 2009 Pre-Release (CTP3) VPC is an unsupported ready to use pre-release of AX 2009. The Virtual PC image of Microsoft Dynamics AX 2009 Pre-Release (CTP3) enables you to demonstrate the features of Microsoft Dynamics AX 2009 using a single PC or laptop computer.

    AX 2009 Pre-Release (CTP3) Demonstration Toolkit Demo Data.

    This version of the Dynamics AX Pre-release Demonstration Toolkit is based on the new Contoso Demo Data Company. The current demo data version will only have base data and no transactions. We are planning to upload a demo data version with transactions shortly.

  • mfp's two cents

    Channel 9 Screencast - MorphX Best Practices

    • 3 Comments

    The new installment of AX screencasts is now available on Channel9. It is a 20 minute video on MorphX Best Practices in Dynamics AX 4.0 - one of my favorite topics!

    Here is the link:

    http://channel9.msdn.com/ShowPost.aspx?PostID=292988

    I've attached the PowerPoint slide deck to this post. The deck contains a transcript of the talk in the slide notes.

  • mfp's two cents

    X++ Debugging Tips and Tricks

    • 3 Comments

    X   Debugging - Full 2

    I hate debugging - I would much rather write code or watch tests execute! Debugging is a waste of time, and often follows an unpredictable path.

    Any developer knows the situation: A bug is discovered, and the cause must be found. Most developers have these war stories on how they fought with a bug. The battle swung back and forth. Theories were tested and discarded, instrumentation was built, log files were examined, Murphy visited and, coffee was spilled. When the stories are told, they typically have a happy ending, so eventually the Eureka-moment occurred and the cause was found. And a debugging lesson was learned.

    One of my toughest debugging session spanned almost 2 weeks. My code was complete and ready for check-in. I had worked on it for about 2 months in a private branch. Before reverse integrating into the main branch I ran all unit tests. A successful run takes about 12 hours. In my case the execution crashed after about 10 hours. I tried to run the single test case that crashed in isolation. It passed. I tried again with the full automation. It still crashed. I then tried disabling the crashing test, just to realize 10 hours later, that now a different test crashed. I then tried running with a debug version of AX32, so I could debug the crash. But the debug build is much slower, so it would take ages before the crash happened. So I needed to isolate the crash in a simpler repro. I tried changing the order of tests, and with every change I made the crash moved somewhere else. I then began trawling the system logs for information about the crash. This lead me to think about memory – perhaps something was leaking memory. I instrumented the test automation, so it would dump the current memory footprint every 10 seconds to a log file. And yes – memory consumption climbed until the crash occurred. But hey; I’m just writing X++ code – what can I possibly do that causes memory to leak? Driving home one day I got the idea that perhaps it was not my code, but the X++ runtime. After all; I was using some of the not-yet-released-at-this-point X++ features pretty heavily (attributes and events). So I wrote a small job, that exercised these areas heavily, and I saw memory climbing slowly but steadily until the crash. Now with a simple repro in hand, it was not hard to solve the issue – but I did require help from my friends in the X++ team. Two weeks had passed before I was ready to submit, my code was exactly the same, and now all test passed.

    This X++ debugging series of posts is dedicated to share X++ debugging tips and tricks. Having a toolbox full of different ways to attack bugs is required to be a productive developer. I’m going to share some of my tricks. If you have tricks of your own, please write a comment, and I will include them in the series. The ultimate goal is that we all efficiently find and solve X++ bugs.


    Trick #1 – Breakpoint in Info.add()

    Trick #2 – xSession::xppCallStack()

    Trick #3 – Conditional breakpoints

    Trick #4 – Reading uncommitted data

    Trick #5 – To be announced


  • mfp's two cents

    Memory consumption of tables variables

    • 3 Comments

    I recently made an interesting discovery, that I want to share with you. I was investigating an recursive algorithm that scaled nicely; until it ran out of memory. For each level of the recursion a new class instance was created – this class contained multiple member variables of type Table (as in CustTable). I decided to measure the memory footprint of this construct, and I was much surprised by my findings.

    Consider these 3 classes – that each stores the same amount of data, but in different ways:

    /// <summary> 
    
    /// Test class containing a table buffer and setting 20 fields 
    
    /// </summary> 
    
    class Class1 
    
    { 
    
        BOMCalcTrans bomCalcTrans; 
    
        public void new() 
    
        { 
    
            bomCalcTrans.ConsumptionVariable = 50; 
    
            bomCalcTrans.ConsumptionConstant = 50; 
    
            bomCalcTrans.CostPriceQty = 50; 
    
            bomCalcTrans.CostPrice = 50; 
    
            bomCalcTrans.CostPriceQtySecCur_RU = 50; 
    
            bomCalcTrans.CostMarkup = 50; 
    
            bomCalcTrans.NumOfSeries = 50; 
    
            bomCalcTrans.SalesPriceQty = 50; 
    
            bomCalcTrans.SalesMarkupQty = 50; 
    
            bomCalcTrans.CostMarkupQty = 50; 
    
            bomCalcTrans.CostMarkupQtySecCur_RU = 50; 
    
            bomCalcTrans.CostPriceSecCur_RU = 50; 
    
            bomCalcTrans.CostMarkupSecCur_RU = 50; 
    
            bomCalcTrans.NetWeightQty = 50; 
    
            bomCalcTrans.CostPriceUnit = 50; 
    
            bomCalcTrans.SalesPrice = 50; 
    
            bomCalcTrans.SalesMarkup = 50; 
    
            bomCalcTrans.SalesPriceUnit = 50; 
    
            bomCalcTrans.SalesPriceFallBackVersion = 'abc'; 
    
            bomCalcTrans.CostPriceFallBackVersion = 'def'; 
    
        } 
    
    } 
    
    /// <summary> 
    
    /// Test class containing a hashTable with 20 key/value pairs 
    
    /// </summary> 
    
    class Class2 
    
    { 
    
        System.Collections.Hashtable hashTable; 
    
        public void new() 
    
        { 
    
            hashTable = new System.Collections.Hashtable(); 
    
            hashTable.Add(fieldNum(bomCalcTrans,ConsumptionVariable), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,ConsumptionConstant), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceQty), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,CostPrice), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceQtySecCur_RU), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkup), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,NumOfSeries), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,SalesPriceQty), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,SalesMarkupQty), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkupQty), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkupQtySecCur_RU), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceSecCur_RU), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkupSecCur_RU), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,NetWeightQty), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceUnit), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,SalesPrice), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,SalesMarkup), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,SalesPriceUnit), 50); 
    
            hashTable.Add(fieldNum(bomCalcTrans,SalesPriceFallBackVersion), 'abc'); 
    
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceFallBackVersion), 'def'); 
    
        } 
    
    } 
    
    /// <summary> 
    
    /// Test class containing 20 member variables with values 
    
    /// </summary> 
    
    class Class3 
    
    { 
    
        InventQty bomCalcTransConsumptionVariable; 
    
        InventQty bomCalcTransConsumptionConstant; 
    
        CostPrice bomCalcTransCostPriceQty; 
    
        CostPrice bomCalcTransCostPrice; 
    
        CostPriceSecCur_RU bomCalcTransCostPriceQtySecCur_RU; 
    
        CostPrice bomCalcTransCostMarkup; 
    
        InventQty bomCalcTransNumOfSeries; 
    
        InventSalesPrice bomCalcTransSalesPriceQty; 
    
        InventSalesMarkup bomCalcTransSalesMarkupQty; 
    
        CostMarkup bomCalcTransCostMarkupQty; 
    
        InventPriceMarkupSecCur_RU bomCalcTransCostMarkupQtySecCur_RU; 
    
        CostPriceSecCur_RU bomCalcTransCostPriceSecCur_RU; 
    
        CostPriceSecCur_RU bomCalcTransCostMarkupSecCur_RU; 
    
        ItemNetWeight bomCalcTransNetWeightQty; 
    
        PriceUnit bomCalcTransCostPriceUnit; 
    
        CostingVersionId bomCalcTransCostPriceFallBackVersion; 
    
        InventSalesPrice bomCalcTransSalesPrice; 
    
        InventSalesMarkup bomCalcTransSalesMarkup; 
    
        PriceUnit bomCalcTransSalesPriceUnit; 
    
        CostingVersionId bomCalcTransSalesPriceFallBackVersion; 
    
        public void new() 
    
        { 
    
            bomCalcTransConsumptionVariable = 50; 
    
            bomCalcTransConsumptionConstant = 50; 
    
            bomCalcTransCostPriceQty = 50; 
    
            bomCalcTransCostPrice = 50; 
    
            bomCalcTransCostPriceQtySecCur_RU = 50; 
    
            bomCalcTransCostMarkup = 50; 
    
            bomCalcTransNumOfSeries = 50; 
    
            bomCalcTransSalesPriceQty = 50; 
    
            bomCalcTransSalesMarkupQty = 50; 
    
            bomCalcTransCostMarkupQty = 50; 
    
            bomCalcTransCostMarkupQtySecCur_RU = 50; 
    
            bomCalcTransCostPriceSecCur_RU = 50; 
    
            bomCalcTransCostMarkupSecCur_RU = 50; 
    
            bomCalcTransNetWeightQty = 50; 
    
            bomCalcTransCostPriceUnit = 50; 
    
            bomCalcTransSalesPrice = 50; 
    
            bomCalcTransSalesMarkup = 50; 
    
            bomCalcTransSalesPriceUnit = 50; 
    
            bomCalcTransSalesPriceFallBackVersion = 'abc';  
    
            bomCalcTransCostPriceFallBackVersion = 'def'; 
    
        } 
    
    }

    Now; let us create 1,000,000 instances of each and store them all in a List(Types::Class), and measure the memory footprint of each type of the 3 classes – running as IL and running as pcode.

    mem

    Notice that using a table as a member variable consumes 6x as much memory as having each field as a member – apparently there is a huge overhead associated with the xRecord+Common functionality.

    My conclusion – apparently you cannot have nice code and low memory consumption at the same time. My advice – be careful when using tables as member variables, especially in batch scenarios, i.e. classes derived from RunBaseBatch or used by same.

  • mfp's two cents

    Debug::Assert in X++

    • 3 Comments

    Update 4th of August 2014: Clarification of the AX runtime behavior when non-developers hit an assert statement.



      defect free software

    “In computer programming, an assertion is a predicate (a true–false statement) placed in a program to indicate that the developer thinks that the predicate is always true at that place.”    

    http://en.wikipedia.org/wiki/Assertion_(computing)

    What would you rather have: A piece of source code with or without assertions? I’d definitely prefer source code with assertions – it makes the code easier to read, debug and troubleshoot.

    My recommendation is to use assertions in X++ when all of the following are true:

    1. The condition can never be false if the code is correct, and
    2. The condition is not so trivial it obviously be always true, and
    3. The condition is in some sense internal to the component, and
    4. The condition can be verified without any method calls.

     

    1. The condition can never be false if the code is correct

    Assertions and error handling are two different things.

    • Assertions should be used to document logically impossible situations in a correctly implemented program. If the impossible occur, then something is wrong – a programming error has been discovered.
    • Error handling – as the name implies – is the implementation of what should happen when an error occurs. Error handling are valid code paths that determines how the software reacts to various erroneous conditions, like incorrect data entered by the user, to system error conditions like out-of-memory.


    In a nutshell, it should be possible to write unit test for error handling – but not for assertions.

    2. The condition is not so trivial it obviously be always true

    Assert statements takes time to write and read – and if the condition they are asserting is obviously always true, then the assertion is pure clutter – and we are better off without it.

    3. The condition is in some sense internal to the component

    A failing assertion is an indication of a problem with the implementation. Something within the component – regardless of input from the outside world – is broken and needs fixing. Typically, I’d use assertions for input validation in private methods, and exceptions in public methods. Conversely, you don’t want consumers of your component to be hit by assertions – regardless of how they use your component.

    4. The condition can be verified without any method calls.

    Assert statements in X++ are a little special, as the X++ compiler always includes assert statements. In other languages (like C#) you can have multiple compiler targets – and typically the release build would not include the assert statements. In AX when a non-developer is hitting an assert statement, then the runtime will suppress eventual errors. I.e. in a production system assert statements have no functional impact.

    Given assert statements in X++ are always evaluated, and thus degrades performance, they should be used with a bit of caution. If the condition can be verified with minimal overhead – for example that a variable has a certain value – then there is no problem. However; if the assertion requires execution of complex logic, RPC or SQL calls then it should be avoided, due to the performance impact. In cases where the performance impact is significant, but you don’t want to compromise on assertions, the assertions can be wrapped inside a call to Debug::debugMode().

    “without any method calls” is just a guiding principles. Sometimes it makes sense to factor the condition into a Boolean method – for reuse or for clarity – here I would not object.

    Examples

    Here is an example of good use of assertion in X++:

    private void markDetailRecordAsEdited(
    RecId _journalControlDetailId,
    RecId _draftConstraintTreeId) { Debug::assert(_journalControlDetailId != 0); Debug::assert(_draftConstraintTreeId != 0); if (! modifiedDetailRecords.exists(_journalControlDetailId)) { modifiedDetailRecords.insert(
    _journalControlDetailId,
    _draftConstraintTreeId); } }

     

    Here is another example where Debug::debugMode() is used:

    private void render()
    {
        ...
        
        if (Debug::debugMode())
        {
            Debug::assert(this.hierarchyCount() > 0);
            Debug::assert(segments != null);
            Debug::assert(totalSegmentCount > 0);
        }
    
        ...
    }

    Closing remarks

    I once saw a t-shirt with this print on the front: “If debugging is the process of removing bugs, then programming must be the process of putting them in”.  I wish the back had read: “Programming with assertions is one way to keep bugs out.”

  • mfp's two cents

    A new AX development book: Microsoft Dynamics AX 2012 Services

    • 3 Comments

    7546EN_cov A new development book has been published by Packt.

    If you are developing with Dynamics AX 2012, you’ll need this book. For two reasons. Firstly, as you’ll need to deal with services. Secondly, because it is a great book.

    The book is 200 pages, and it does a great job of explaining how to build and how to consume services.

     

    I think the book is great for several reasons:

    • It is technical. The level of contents and technical depth is comparable to Inside Dynamics AX 2012.
    • It is unique. The contents is written for this book. You will not find it anywhere else.
    • It is self-contained. Whenever a concept is used, it will be explained – even when it is not directly related to services. So be prepared for excursions to the lands of CIL, WCF, Runbase, AX Security, Visual Studio, UI builders etc.
    • It is actionable. Each chapter shows step-by-step, line-by-line what to do in various scenarios. Urging you to try it out yourselves.
    • It is opinionated. I’m not sure how they managed, but the book somehow captures the atmosphere we had when building AX 2012 – we knew we were on to something special. The book deals out appraisals where due, and explains why AX 2012 is a huge leap forward for services.
    • It is jovial. The tone in the book is welcoming and direct – I think the most used word is “you”. This is quite refreshing for a technical book.

     

    A big “Thank You!” to the team behind the book for increasing the AX community’s knowledge: Klaas Deforche, Kenny Saelen, Palle Agermark, José Antonio Estevan and Tom Van Dyck.

  • mfp's two cents

    Dynamics AX 2012 R2 achieves "Compatible with Windows 8" certification.

    • 2 Comments

    clip_image001Microsoft Dynamics AX 2012 R2 has successfully passed compatibility testing and has achieved the “Compatible with Windows 8” certification. 

    The Compatible with Windows 8 certification means that the product has passed Microsoft testing criteria for compatibility with Windows 8. The Microsoft Windows Compatibility and Certification program defines a set of compatibility test cases that must be completed successfully-covering areas of Compatibility & Resiliency, Adhere to Windows Security Best Practices, Support Windows Security Features, Adhere to System Restart Manager Messages and perform a Clean, Reversible Installation. The testing is performed using Microsoft’s automated Application Certification framework and tools.

    Microsoft Dynamics AX 2012 R2 is now listed among the products that have been certified on the Microsoft Windows compatibility center site.

  • mfp's two cents

    Pre-order Inside Microsoft Dynamics AX 2012

    • 2 Comments

    We are heading for a true power demonstration from Microsoft in the fall of 2012. A long line of exciting products will be released, including Windows 8, Windows Server 2012 and Microsoft Surface

    …and the probably most anticipated of them all:
    Inside Dynamics AX 2012.

    Pre-order your copy today.

    Abstract:

    Dig into the architecture and internals of Microsoft Dynamics AX 2012 - with firsthand insights from the team that designed and developed it. Targeted for solution developers and system implementers, this guide focuses on programming and customization capabilities - including key architectural principles, the application model, framework, and tools. Topics include: Architecture and development environment, including MorphX Microsoft Visual Studio tools for Microsoft Dynamics AX X++ programming language Microsoft SQL Server reporting and analytics Models Core development concepts Extending and customizing Microsoft Dynamics AX Performance and security considerations Workflow Best practices Note: Readers should have working knowledge of SQL and OOP concepts to gain max benefit from this book.

Page 5 of 19 (187 items) «34567»

mfp's two cents

...on Dynamics AX Development!