Microsoft Dynamics vendors provide comparisons and opinions to professionals in the ERP/Accounting software selection process

 
 

Synoptek

Microsoft Dynamics 365 for Finance and Operations Coding Standards


Email | Print

This blog outlines the requirements and guidelines for code development in Microsoft Dynamics 365 for Finance and Operations. Coding standards sometimes referred to as programming styles or coding conventions. It is an important asset to developers and also contributes to the quality of overall project delivery.

These standards cover what is important when writing code in Microsoft Dynamics 365 for Finance and Operations.  The document is regularly updated and if you have any ideas or suggestions that improve or add to these standards, please make your manager aware of them so they can be considered.

Development Requirements

Models and Packages

Model Definition

A model is a group of elements, such as metadata and source files, that typically constitute a distributable software solution and includes customization of an existing solution. A model is a design-time concept, for example, a warehouse management model or a project accounting model. A model always belongs to a package.

Package Definition

A package is a deployment and compilation unit of one or more models. It includes model metadata, binaries, and other associated resources. One or more packages can be packaged into a deployable package, which is the vehicle used for deployment on run time environments.

  • Always create a new model / or ask for model to be used for development purposes
  • Always select “create a new package” as an option while creating a new model (Extension based approach)
  • Model name should be one word with no space(ex, Synoptek)
  • Display name may have spaces (ex, Application Suite Extension)
  • Use ‘CUS’ layer
  • Notify all developers when creating a new model

In these environments the application layers are used in the following manner:

Each layer has a corresponding patch layer that can be used to incorporate updates to the application or to store conflicts when you import models into a layer.

Development Topology

Each project will have different development topology. Make sure you are working on correct development topology.

  • Local DEV-1, Local DEV-2, Local DEV-N (Optional)
  • Cloud-hosted Environment (Optional)
  • Sandbox: Develop and Test (Tier 1 environment)
    • All customers are provided with one free “develop and test” environment hosted in Microsoft’s Azure subscription. Under “Develop and Test”, there are two types of environments, Develop and Build and Test.
  • Sandbox: Standard Acceptance Test (Tier 2 environment)
  • Production (Tier 2 environment)

Projects

  • Refer new project to the custom model created for development purposes
  • All projects name should start with the Unique Ticket number (UT) or in case there is no task number provide either ask your supervisor for the same or name it in a meaningful manner which will help understand other developer purposes of customization followed by a short description.
  • For example: “UT####_shortdescription” or likewise.

Development Guidelines

  • Before starting a development make sure the development environment is connect to TFS and you have performed get latest for all objects.
  • Object extensions need to be named as xxxxx.ExtentionSXA
  • New classes for event handlers and delegates need to be named as: SXAxxx_EventHandler
  • For classes and tables, the naming convention should follow the class or table name as follows

[Extension of(tablestr(InventSum))]

Public final class SXAInventSum_Extension

  • Do not use ‘hard coded’ values/Labels. Values from the database should be retrieved from the database. Create a new label file in custom model and ask if they need to be created in multiple languages.
  • Create customization specific extension rather than using existing customization.
  • Fixed strings should use a macro
  • All modifications should have comments describing what the code is doing including Unique Ticket number/ Task description.
  • Do not change the method access modifiers to ‘public’ for -pre or -post event handlers; use chain of command
  • If you are modifying any existing object then make sure to have source code backup taken before starting development.
  • Keep a practice to also take DB backup before starting new development or modification.
  • If required always Import Model or DB backup on LCS only.
  • Always take the backup of your daily work done in your local system, and perform build and synchronization of your project. Make sure your project is error free.
  • After deploying the SSRS report, perform model build and verify deployed reports over a screen.

Capitalization Styles

Camel Case

The first letter in the identifier is lowercase and the first letter of its addition is a capital.

Example: dynamicsAx.

Pascal Case

The first letter in the identifier is a capital and the first letter of its addition is a capital.

Example: DynamicsAx.

Upper Case

All letters in the identifier are capitals.

Example: DYNAMICSAX.

Lower Case

All letters in the identifier are lowercase.

Example: dynamicsax.

Objects Casing Conventions

Commenting Changes in the Code

All changes made in the code are commented on.

General

To be able to recognize modifications they must be commented in the code.  A comment line must at least have the following information:

<Layer>, <AxProjectname>, <dd.mm.yyyy>, <User Id>.<Code>

Example: //CUS, FDD0014_ItemChanges, 19.08.2014, ParasD.N

Codes:  

Where applicable the documentation Header template must be applied to methods and filled incorrectly. You can directly write forward-slash three times to add the header template.

When to use Codes ‘Sn’ & ‘En’

When commenting new code or replacing code in an existing custom method, then the code ‘Sn’ and ‘En’ should be used.

Example: Inserting comments for new code

//CUS, FDD00001_DevStandards, 19.08.2014, ParasD.Sn

//Change description: <Note>

documentStatus = DocumentStatus::PackingSlip;

salesStatus          = SalesStatus::None;

//CUS, FDD00001_DevStandards, 19.08.2014, ParasD.En

Example: Inserting comments for code that is being replaced

//CUS, FDD00002_DevStandards, 19.08.2014, JigalP.Sn

//Change description: <Note>

 /*

documentStatus = documentStatus::PackingSlip;

salesStatus    = salesStatus::None;

*/

documentStatus = DocumentStatus::ProjectPackingSlip;

salesStatus          = SalesStatus::Backorder;

//CUS, FDD00002_DevStandards, 19.08.2014, JigalP.En

Note, you can add an extra line to explain the modification.

//Change description: <Notes>

When to Use Codes ‘So’ & ‘Eo’

The ‘So’ & ‘Eo’ codes are used to comment out multiple rows of code that is not to be executed.

Example: Commenting out code

//CUS, FDD00003_DevStandards, 19.08.2014, JigalP.So

/*

_salesTable_Upd.DeliveryDate    = _salesTable_Src.DeliveryDate;

_salesTable_Upd.ContactPersonId = _salesTable_Src.ContactPersonId;

*/

 //CUS, FDD00003_DevStandards, 19.08.2014, JigalP.Eo

When to Use Codes ‘N’ & ‘O’

The ‘N’ code will be used for new methods on existing objects, commenting on the classDeclaration for new elements, and adding one new line of code to an existing method.

Example: Commenting a classDeclaration for a new element.

//CUS, FDD00004_DevStandards, 19.08.2014, ParasD.N

class SXADEVStandards

{

SalesId     salesId;

}

Example: Commenting a new method to an existing element

//CUS, FDD00005_DevStandards, 19.08.2014, ParasD.N

private void sxadevStandardsMethod()

{

 

}

 

Example: Commenting one new line of code to an existing method.

private void run()

{

this.sxadevStandardsMethod();

//CUS, FDD00006_DevStandards, 19.08.2014, ParasD.N

this.sxadevStandardsMethod1();

}

 

The ‘O’ code will be used for commenting out one line of code.

 

Example:

class SXADevStandards

{

SalesId     salesId;

PurchId     purchId;

 

//CUS, FDD00007_DevStandards, 19.08.2014, ParasD.O

//CustAccount     custAccount;   

}

SQL statements

The following rules should be applied when writing select statements:

  • Where possible make use of SUM(), COUNT(), UPDATE_RECORDSET and DELETE_FROM as these are far more efficient than while selects. Note that for the UPDATE_RECORDSET and DELETE_FROM statements to be effective developers may also need to apply skipDataMethods(), skipDeleteActions() and skipDatabaseLog() to the relevant data buffer.
  • If efficiency is likely to be an issue (wide tables and very large tables) then do specify a field list to restrict the amount of data returned from the database. However, note that this can lead to bugs in the code later on when the additional code is added.
  • Use firstOnly where applicable to increase performance. If you are going to use only the first record, or if only one record can be found, the firstOnly qualifier optimizes the select statement for that circumstance.
  • WHERE clause. If the where clause does not include all the fields of the index you plan to use then it must at least contain all the fields from the beginning of the index. If you only include fields that are in the middle of the index definition then SQL Server must scan the entire index to find the record(s) you want. For example, if an index contains Item Id, Item Group and Item subgroup in that order and the where clause only contains item group and item subgroup then you will force a full index scan. You must either change your clause, use a different index or create a new one.
  • The select statement should have a comment to show that the developer had an index in mind when the where clause was written.

Example:

select firstOnly salesTable

// index - SalesId

where salesTable.salesId == _salesId;

  • For a select statement, the && and II should be on the left

Example:

select firstOnly salesLine

// index – SalesLineIdx

where salesLine.SalesId == _salesId

&& salesLine.ItemId == _itemId

&& salesLine.InventDimId     == _inventDimId;

  • The relational operators (i.e. ==) should line up.

Do’s and Don’ts

General Best Practices

  • Include a try catch around all transactions that could result in deadlock.
  • Run code on AOS whenever possible.
  • Keep database transactions as short as possib le.
  • Use throw instead of ttsAbort.
  • Use

Ok = checkfailed(“Error message”) ;

Instead of:

Ok = false;

return ok ;

  • Do not write code in the validate methods that would initialize or modify the values in fields. Also, validation logic should not be included in insert, update, or delete  event handlers. Try to place validation logic on table pre event handlers like onvalidatingField and onvalidatingWrite instead of placing them on form data source event handlers.
  • Avoid autoDeclaration of form controls. If you need to enable/disable or set some properties on form controls, always access data source fields.

Example: salesTable_ds.object (fieldnum(SalesTable,SalesId)).enabled(false);

Avoid: salestable_SalesId.enabled(false) where salestable_SalesId is control name on the form.

  • When writing complex join queries, try to use ‘exists join’ when values from all the joined tables are not required.
  • Make sure when you create new tables, the table properties match and adhere to the purpose of the table.

Example: Table Group property.

  • When using select queries, try to use a fieldlist instead of select * from tablename. This will help optimize the query time.
  • Make sure that the number of variables declared at the global level is minimum to increase performance. Inline variables declaration is valid in Dynamics 365 for Finance and Operations.
  • Make use of the access modifiers: ‘public’, ‘protected’ and ‘private’. All methods must be specified through an access modifier.
  • Single point of return
    • Methods must have a single point of return (return statement). The only exception to the rule is to avoid complex 'if then else statements’. Methods of type 'void' never have a return statement.
  • Minimal usage of ‘member variables’, preference ‘locals’. Simple reason: Keep locally what is local to avoid errors. Arguments passed in a method should not be modified, and only the value should be used.
  • Updating/Inserting/Deleting Records:

Commands

update_recordset

insert_recordset

delete_from

  • If multiple records must be adjusted, use UPDATE_RECORDSET instead of ‘select for update’. The advantage is that the statement: UPDATE_RECORDSET, only executes 1 database action, while a ‘while select statement’ uses for each record to update a database action.

Example:

while select forUpdate custTable

// Index - <suitable index>

where custTable.CustGroup ==  ‘Group1’

{

custTable.SomeField = 1;

custTable.update();

}

Better:

update_recordset custTable

// Index - <suitable index>

setting SomeField = 1

where custTable.CustGroup ==  ‘Group1’

  • The same goes for ‘DELETE_FROM and INSERT_RECORDSET’. They use 1 database action.
  • Make maximum use of 'joins'. The following examples do the same functional, but the first example retrieves all 'Sales Lines’ from the database. The second example retrieves only those 'Sales Lines' from the database that match a specific product group. This results in less network traffic and less processor time (thus performance is better).

Example:

InventTable        inventTable;

salesLine              salesLine;

// this code will work fine

while select salesLine

where salesLine.Scrap == false

{

if (salesLine.inventtable().IyemGroupId != ‘XXX’)

{

// do something

}

}

// this is much better

while select salesLine

where salesLine.Scrap == false

join inventTable

where inventTable.ItemId            == salesLine.ItemId

&& inventTable.ItemGroupId !=  ‘XXX’

{

// do something

}

  • Exceptions should not be used to influence the outcome of the program (process). For example, A customer, for which the credit limit is reached, is no exception but a regular program (process) situation. Exceptions should only be used as real exceptions. Such as 'duplicate keys', 'file not found' etc.

Developer Testing

Following points should be taken care while developer testing:

  • Developers should ensure that elements of the software are sufficiently unit/developer tested at the coding stage in the Development Environment before being passed on to system testing on the Test Environment
  • Developers should ALWAYS ensure that 100% code coverage is achieved through the tests that he/she executes. For example, if a condition is inserted into the code then the developer must ensure that the tests will at the very least cause the ‘true’ condition and the ‘false’ condition to be executed at least once. It is the responsibility of the developer to find out/understand how the application is used from a user's point of view in order that he/she can carry out the necessary tests.

Code Review Checklist

When you are asked to technical QA a piece of development, the following checks must be made:

Standards

  • Naming convention
  • Tables, Maps, and Views
  • X++ coding best practices
  • Error handling
  • Performance and optimization
  • Documentation

The following supporting documentation is required:


About the Author - Shailee Shah

A dynamic IT professional with progressive experience in Microsoft Technologies, with expertise in Microsoft Dynamics 365 Finance and Operations and Dynamics AX as a Technical Consultant.

Ask This Expert a Question / Leave a Comment