Ax2012 Enus Dev 2
Ax2012 Enus Dev 2
Ax2012 Enus Dev 2
DEVELOPMENT II IN
MICROSOFT DYNAMICS AX 2012
Table of Contents
Introduction
0-1
1-1
2-1
3-1
4-1
5-1
6-1
ii
Table of Contents
Display Method Authorization ............................................................................. 6-8
Summary ............................................................................................................ 6-9
Quick Interaction: Lessons Learned ................................................................. 6-11
Solutions ........................................................................................................... 6-12
iii
iv
Introduction
INTRODUCTION
Welcome
We know training is a vital component of retaining the value of your Microsoft
Dynamics AX 2012. investment. Our quality training from industry experts
keeps you up-to-date on your solution and helps you develop the skills necessary
for fully maximizing the value of your solution. Whether you choose Online
Training, Classroom Training, or Training Materials; there is a type of training to
meet everyone's needs. Choose the training type that best suits you so you can
stay ahead of the competition.
Online Training
Online Training delivers convenient, in-depth training to you in the comfort of
your own home or office. Online training provides immediate access to training
24 hours-a-day. It is perfect for the customer who does not have the time or
budget to travel. Our newest online training option, eCourses, combine the
efficiency of online training with the in-depth product coverage of classroom
training, with at least two weeks to complete each course.
Classroom Training
Classroom Training provides serious, in-depth learning through hands-on
interaction. From demonstrations to presentations to classroom activities, you
receive hands-on experience with instruction from our certified staff of experts.
Regularly scheduled throughout North America, you can be sure you will find a
class convenient for you.
Training Materials
Training Materials enable you to learn at your own pace, on your own time with
information-packed training manuals. Our wide variety of training manuals
feature an abundance of tips, tricks, and insights you can refer to again and again:
0-1
Challenge Yourself!
Level 3 exercises are the most challenging. These exercises are designed for the
experienced student who requires little instruction to complete the required task.
Step by Step
Level 1 exercises are geared towards new users who require detailed instructions
and explanations to complete the exercise. Level 1 exercises guide you through
the task, step by step, including navigation.
0-2
Introduction
Documentation Conventions
The following conventions and icons are used throughout this documentation to
help you quickly and effectively navigate through the information.
CAUTION: Cautions are found throughout the training manual and are preceded by
the word CAUTION in bold. Cautions are used to remind you of a specific result of a
specific action which may be undesirable.
HINT: Hints are found throughout the training manual and are preceded by the word
HINT in bold. Hints are used to suggest time-saving features or alternative methods for
accomplishing a specific task.
NOTE: Notes are found throughout the training manual and are preceded by the word
NOTE in bold. Notes are used to provide information which, while not critical, may be
valuable to an end user.
BEYOND THE BASICS: Advanced information found throughout the training manual
is preceded by the words BEYOND THE BASICS in bold. Beyond the Basics provides
additional detail, outside of standard functionality, that may help you to more optimally
use the application.
EXAMPLE: Examples are found throughout the training manual and are preceded by
the word EXAMPLE in bold. Examples bring to light business scenarios that may better
explain how an application can be used to address a business problem.
0-3
Student Objectives
What do you hope to learn by participating in this course?
List three main objectives below.
1.
2.
3.
0-4
Introduction
X++ is the primary programming language used in the MorphX Development
environment. When you develop with X++, refer to Microsoft Dynamics AX
Developer Help, available from the Help menu for detailed information. The
following are important features of X++:
1-1
Characteristics of X++
The following are characteristics of X++:
Interpreted and Dynamic: Benefit from faster development cycles prototyping, experimentation, and rapid development, versus the
traditional compile, link, and test cycles.
Scenario
Systems developers in a large development environment have joined a team that
is customizing a Microsoft Dynamics AX application. This is the team's first
project in X++ so they need to learn what development tools are available to
them within Microsoft Dynamics AX.
Development Tools
All elements that comprise the Microsoft Dynamics AX application (classes,
forms, tables, and more) are organized in the Application Object Tree (AOT).
This is a central, organized index to all application elements, displayed in a
graphical tree view.
Microsoft Dynamics AX customizations are developed by modifying one or
more elements from the AOT. These elements are usually assembled in a project,
or, in other words, a container for elements that implement an application in
Microsoft Dynamics AX. Projects help manage development efforts by
organizing units of functionality.
As developers create new projects or modify existing projects, they use a series
of development tools. These tools include the following:
1-2
X++ Editor
X++ Compiler
X++ Debugger
Visual Studio
There are several toolbar buttons in the heading of the X++ editor window.
Many of these functions can be started using keyboard shortcuts. Moving the
pointer over these buttons reveals the button's function. These buttons are
described in the following table:
1-3
Keystroke
Description
New
Ctrl + N
Save
Ctrl + S
Undo
Ctrl + Z
Redo
Ctrl + Y
Go
F5
Toggle Breakpoint
F9
Enable/Disable
Breakpoints
Ctrl + F9
Remove all
breakpoints
Ctrl + Shift
+ F9
Compile
F7
Lookup label/text
Ctrl + Alt +
Space
Script
Alt + R
Add to Version
Control
Check In
Alt + I
Check Out
Alt + O
Alt + U
History
Get Latest
Help
1-4
F1
Toggle line
numbers
Toggle change
tracking margin
Code type
Blue
Reserved words
Green
Comments
Dark Red
Strings
Bright Red
Numbers
Purple
Labels
Black
Everything else
Jobs
A job is a stand-alone block of code in Microsoft Dynamics AX that can be run
from the X++ editor. Jobs are used primarily for testing code during development
and running batch processes within Microsoft Dynamics AX that affect large
amounts of data.
This course uses jobs as a mechanism to write code and run it directly from the
X++ editor. Most code for applications is written in classes, methods, tables, and
forms. However, jobs are useful for testing blocks of code before inserting them
into methods or classes.
The following is default code when you create a new job in the AOT:
static void Job1(Args _args)
{
}
NOTE: Unlike classes and other elements in the AOT, jobs can only be renamed
in the Code Editor. Most other AOT elements can have their name changed in the
Property window, or directly on the AOT node.
Create a new job by right-clicking the Job node on the AOT, and selecting New
Job. The X++ editor opens, displaying the new job. The job can be renamed in
the text editor or in the AOT. In the example above, the job is called Job1 and
can be renamed by change Job1 to the required namein the text editor.
1-5
Description
Error
Warning
Best Practice
Task
1-6
Filter buttons: At the top of the window, there are buttons to toggle
the four different compile result types on and off. Toggling the result
type off will remove results of that type from the Compiler Output
window's list.
Reset button: This button will reset the contents of the Compiler
Output window.
Edit button: This button will open the X++ editor or Property sheet
corresponding to the selected compile result.
Debugger
Microsoft Dynamics AX includes a powerful stand-alone debugging tool for
X++ code. Use the Debugger to debug X++ code running on the:
NOTE: Managed code and X++ compiled into Intermediate Language (IL) are
debugged using the Visual Studio debugger. This topic will be covered in the
Visual Studio Integration chapter of the Development III course. This topic only
covers debugging standard X++ code.
Use the Debugger to:
1-7
1-8
Description
Code
Variables
Call Stack
Watch
Breakpoints
Output
1-9
Moving the pointer over these buttons reveals the button's function and a
keyboard shortcut.
The more generally used buttons are described in the following table, together
with the keyboard shortcut:
Button
Keystroke
Description
Go
F5
Stop
Debugging
Shift + F5
Insert/Remove
Breakpoint
F9
Enable/Disable
Breakpoint
Ctrl + F9
Remove all
breakpoints
Ctrl + Shift +
F9
Step Over
F10
Step Into
F11
Step Out
Shift + F11
Run to Cursor
Ctrl + F8
1-10
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
Compare
Use the compare tool to highlight differences between versions of a single
element:
1-11
You can also use the compare tool to move code and element properties between
layers. When a difference is found, a black arrow is displayed in the left margin
of the lower-right pane. The icon will delete code from the current layer, or paste
code in to the current layer. This feature is useful when upgrading code.
NOTE: You can view code in a different layer by right-clicking the element and
selecting Layers.
1-12
X++ Attributes
Microsoft Dynamics AX supports attributes being assigned to X++ code. This
allows for a rich set of metadata to be built. It describes methods and types that
are defined within X++ code.
Attributes are defined as classes that are derived from the SysAttribute class.
The following code for the SampleAttribute class is an example of an attribute
class:
public class SampleAttribute extends SysAttribute
{
str sMetadata; // A field in classDeclaration.
public str GetMetadata()
{
return sMetadata;
}
public void new(str _sMetadata)
{
super();
sMetadata = _sMetadata;
}
}
Attributes can then be used on other classes and methods. The following example
shows the structure of a new Attribute class, AuthorAttribute:
class AuthorAttribute extends SysAttribute
{
str author;
public str Author()
{
return author;
}
1-13
1-14
1-15
1-16
Each of the nodes in the Type Hierarchy Context window is also a hyperlink
that will open the Type Hierarchy Browser for an element.
Reverse Engineering
Reverse engineering is used by partners to easily retrieve detailed information
about the structures and relationships of the Microsoft Dynamics AX business
logic and data model. The goals of reverse engineering are to extract
relationships, and integrate and view collections in Microsoft Office Visio as
UML diagrams.
The feature handles both reverse engineering of the data model and the object
model.
You can use the Reverse Engineering tool to visualize data and object models
generated from projects in Microsoft Dynamics AX. These models are created as
Microsoft Visio documents.
To open the Reverse Engineering tool, right-click a project and select Add- ins >
Reverse engineer.
The information Microsoft Dynamics AX provides Visio depends on whether
you are creating a data or an object model from a project. The following table
shows what is provided for each model type:
Data Model
Object Model
Tables
Table Fields
1-17
Object Model
1-18
Best Practices
Best practices involve understanding all the things that you should do and
distinguishing them from the things that you can do.
Best Practices in Microsoft Dynamics AX apply to programming in a standard
application, certified solutions, and they are recommended for any Microsoft
Dynamics AX partner or an end-user enhancing or customizing Microsoft
Dynamics AX.
Some benefits of using best practices include:
Naming Conventions
Naming conventions contribute to consistency and to making the application
easier to understand.
Where you can, application element names should be constructed hierarchically
from three basic components:
{business area name} + {business area description} + {action performed (for
classes) or type of contents (for tables)}
Examples:
CustJournalPrintOutInvoice
PriceDiscAdmDelete
PriceDiscAdmSearch
PriceDiscAdmName
PriceDiscAdmTrans
1-19
All texts that appear in the user interface must be defined by using a
label.
Code Placement
The placement of code depends on the code you are writing. The preferred
procedure in an object-oriented programming language is to place code in a class
that can be reused and called when it is needed.
You can place code in classes to have better control over where the code is
executed. Performance considerations frequently dictate where code should run,
and so controlling this is desirable. Code included in forms always runs on the
client, and this could cause excessive client/server communication where it might
not be necessary.
When deciding where to place code, ask the following questions:
BEST PRACTICE: Always place the code nearest to the source so that it can be
more easily manipulated. For example, code that manipulates a table should be
placed in that table's methods.
1-20
1-21
Challenge Yourself!
Create a job that prints the message "Microsoft Dynamics AX is fantastic."
Step by Step
1.
2.
3.
4.
1-22
Challenge Yourself!
Set a breakpoint in the code in the job from the previous lab. Run the code and
step through it.
Step by Step
1. Set a breakpoint in your job on this line of code:
static void <your jobname>(Args _args)
2. Save the code by using a keyboard shortcut.
3. Run the code.
NOTE: You should enter the Microsoft Dynamics AX Debugger at this point. If
not, ask your instructor.
4. Step through the code by using the icon on the toolbar or F11.
1-23
Challenge Yourself!
Make the data model for the Project Accounting module in Microsoft Visio by
using the Reverse Engineering tool in Microsoft Dynamics AX.
Step by Step
1.
2.
3.
4.
5.
6.
7.
8.
9.
1-24
Challenge Yourself!
Create an XML file by using the XML documentation generation, for the Credit
Limit development project.
Step by Step
1. Open the development project tree.
2. Find the Credit Limit project.
3. Right-click the project, and select Add-ins > Extract XML
documentation.
4. Enter a file name.
5. Click OK.
6. Click Close to close the infolog window.
7. Review the file created.
1-25
Summary
This lesson introduced some development tools that you will use to make
modifications to Microsoft Dynamics AX. You learned how to use the X++
editor to write code; how to compile code and review errors with the Compiler;
and how to create breakpoints and step through executing code with the
Debugger. Other tools like the Compare, Type Hierarchy Browser, Type
Hierarchy Context, and Reverse Engineering tools were also introduced.
The next lesson will show how to use these tools by creating a Calculator Class
that uses four basic methods.
1-26
1-27
2.
3.
1-28
Solutions
Test Your Knowledge
1. What are the three main Object-Oriented Programming components?
MODEL ANSWER:
Classes are blueprints that describe the objects derived from them. A class is
a model classification of the methods and variables in a specific type of
object. Objects are instances of classes. Each instance has data members and
logic (methods) defined in the class. Methods are functions (subroutines)
associated with a class or an object. An object implements its behavior with
methods.
2. What are the functions of the six Debugger window panes?
MODEL ANSWER:
The Code window pane displays the X++ code that is currently being
debugged. The Variables window displays the value of the variables in the
current block of code. Modified variables appear in different colors to
indicate change. The Call Stack window tells you which method is currently
being worked on. The Watch Status window displays a user-defined range of
variables. The Breakpoint window consists of a header with two columns and
the list of currently defined breakpoints. The Output window in the Microsoft
Dynamics AX Debugger has separate views that display text written to the
window from X++ code and kernel code. These views organize the
information that is sent to the Output window.
3. What do you use the X++ editor for?
MODEL ANSWER:
The X++ editor is used for editing and creating X++ code. This editor
contains many functions in the toolbar, through keyboard shortcuts, and also
by using the context (right-click) menu. The X++ editor has two panes: the
left side shows a list of the current methods and the right side shows the
current X++ code.
1-29
1-30
Introduction
This course explains how to use control statements in X++. These statements
control the logic flow in the program. This course also describes how to use some
built-in functions in Microsoft Dynamics AX to communicate with the end-user.
Scenario
Isaac, the Systems Developer, is implementing a project that requires a
modification to code. He has been asked to become familiar with the typical
statements in X++ used to control program flow, and to communicate with the
user.
2-1
Introduction to Variables
Variables hold data when a block of code executes. Variables all have scope.
Scope is the area where the variable can be accessed. Some different types of
scope include:
Scope
Description
Use the type of variable that matches the type of data that you want to store. You
can name the variable, as long as you do not use names the system already uses,
such as commands or keywords. A list of reserved words is located in the
Developer Help under the "Keywords" lesson.
BEST PRACTICE: Do not use names like string1. Always give a variable a
meaningful name so its use is easier to identify when reading the code.
Declaration
All variables must be declared before use. When a variable is declared, a small
amount of memory is reserved. The syntax of the declaration is the same,
whether it is a simple variable or an object variable.
You cannot mix variable declarations with other X++ statements. Variables must
be declared before the statements.
There are two rules to use when declaring variables:
You might want a variable to have a value other than the default when the
variable is declared. X++ supports initialization of variables in the Declaration
statement. Initialization is performed by adding the assignment-statement to the
variable declaration. For example:
int a = 10; // a is assigned the value 10
2-2
Description
Example
Declaration
Keyword
String
A string is a number of
characters. X++ supports several
types of strings: Left aligned,
right aligned, fixed length or not
fixed length. The maximum
length of a string is 999
characters.
Name
str
Integer
1090
int
Real
3.14
real
Date
10\29\1978
date
UTC
DateTime
9/28/2008
07:11:02 am
utcDateTim
e
Enum
NoYes
Must be
declared as
a Base
Enum first
Boolean
TRUE
boolean
Time
15:23:08
timeOfDay
GUID
{3F2504E04F89-11D39A0C0305E82C3
301}
guid
Int64
5637144579
Int64
2-3
variableIdentifier;
The data type can be any of the data types in X++. Here are some examples:
int
real
str
str 30
date
boolean
integerVariable;
realVariable;
unboundStringVariable;
boundStringVariable; // max of 30 chars
dateVariable;
booleanVariable;
When declaring a variable, you can also declare them as an extended data type.
This is a good practice because it can highlight errors in code at compile time
when a variable is used incorrectly.
It is common to name the variable the same as the extended data type, when
possible.
custAccount
transDate
amountMST
custAccount;
transDate;
amountDebit, amountCredit;
Initializing Variables
The following statements show how to declare the variables for use later in the
code. You can declare several variables of the same type with different names.
You can also assign a value to the variable when you declare it or later in the
code.
int
int
2-4
counter, toCount;
fromCount = 1;
Description
Array
An array is a list of items with the same data type and the
same name; only the index differs.
Container
Classes
Tables
Arrays
Arrays can be declared by adding brackets ( [ ] ).
You can set the maximum number of array elements by putting the number in the
brackets.
Array values can be set by specifying the index when assigning the value.
real
real
realLimitedArray[2] = 3.142;
Containers
A container variable can contain different types and values of simple and
extended data types, including arrays and other container variables. Classes
cannot be put into containers.
There are many functions that manipulate container variables. The following
functions are available:
Function
Description
conPeek
conDel
conNull
2-5
Description
conFind
conIns
conPoke
conLen
The following examples have a container variable that contains four values,
including three different data types.
container c;
// the container is declared
int
i, j;
str
txt;
c = [10, 20, "test"]; // the container has 3 values set
print conPeek(c, 3);
// the third element is printed
[i,j,txt] = c; // other variables are set from the
container
Operators
Operators are used to manipulate variable and field values and to control the
logical program flow based on the values in variables and fields. The following
types of operators are available.
Assignment Operators
Assignment operators modify the contents of a variable or field. The following
table defines available operators.
2-6
Operator
Description
+=
++
-=
--
Term
Description
Plus
Minus
Multiply
Divide
DIV
Integer
division
MOD
Integer
remainder
Not
&
Binary
And
Binary
XOR
Binary Or
<<
Left shift
>>
Right shift
Ternary
operator
The following are some examples of these arithmetic operators. For all examples,
the variable 'i' is an integer.
Evaluated Expression
Return Value
i++;
i--;
i += 2;
i -= 3;
i = 3 << 3
i = 24 (i = 3*2*2*2)
2-7
Return Value
i = 24 >> 2
i = 6 (i = 24/2/2)
i = 80 DIV 13
i = 80 MOD 13
NOTE: View more examples of arithmetic operators in the "X++ Online Help
Guide."
Relational Operators
Relational operators, except for '!', are placed between two expressions. The
following table defines available relational operators:
2-8
Operator
Term
Description
==
equal
>=
greater than
or equal
<=
less than or
equal
>
greater than
<
less than
!=
not equal
&&
and
||
or
not
like
like
Return Value
9 != 10
Operator Precedence
You can use X++ to create complex statements using multiple operators when
data types on all parts of the statements are equivalent. When multiple operators
are used in one statement, precedence for the operators must be in place for
statement evaluation. The following table lists the precedence for operators. The
highest precedence is at the beginning of the table; precedence gets lower as you
move down the table.
Operator Type
Operator Syntax
postfix operators
unary operators
creation
new (type)expr
multiplicative
*/
additive
+-
relational
equality
== !=
bitwise AND
&
shift
<< >>
bitwise exclusive OR
bitwise inclusive OR
&& ||
conditional
?:
assignment
= += -=
2-9
Conditional Statements
Conditional statements in programming define conditions under which certain
functions are performed. Conditional statements use logical expressions that are
evaluated and return a value of either true or false. There are three primary
conditional statements:
If statement
Switch statement
Ternary operators
If
The if statement is the simplest control statement. It checks whether a condition
is true or false. If the condition is satisfied, all the code within the braces '{}' is
executed. The syntax for an if statement is as follows:
if (condition)
{
//if true these statements are executed
}
2-10
int
int
int
i = 12;
j = 10;
max;
if (i > j)
{
max = i;
}
else
{
max = j;
}
The previous conditional formulas allow for only two alternative outcomes. A
program might have to check more than two alternatives. To check for multiple
alternatives, you can use an ifelse...if statement. The syntax for this statement
is as follows:
if (condition1)
{
//statement1
}
else
{
if (condition2)
{
//statement2
}
else
{
//statement3
}
}
2-11
passExam = true;
passHomeWork = false;
studentStatus;
if (passExam == true)
{
if (passHomeWork == true)
{
studentStatus = "Passed";
}
else
{
studentStatus = "Failed";
}
}
else
{
studentStatus = "Failed";
}
Ternary Operator
This conditional statement behaves exactly like an ifelse statement. The main
reason to use the ternary operator is convenience in coding. Its syntax is as
follows:
condition ? statement1 : statement2;
2-12
i = 12;
j = 10;
max;
max = i > j ? i : j;
Switch
A switch statement acts as a multi-branch control statement that defines an
expression and whose result leads to a specific program execution. The switch
statement considers the result and executes code, depending on possible
outcomes of the expression. These are known as cases. Each of these cases is
listed in the body of the statement.
Following the colon after each case are statements that execute if the expression
satisfies the case. There can be any number of statements following a case in a
switch statement. The body of a switch statement is enclosed in braces '{}'. The
following shows the syntax for a switch statement:
switch (expression)
{
case 'Choice1':
case 'Choice2':
case 'Choice3':
Statement1;
Statement2;
break;
Statement3;
break;
Statement4;
Statement5;
Statement6;
break;
default : DefaultStatement;
}
The break; statement tells the program to leave the switch statement and
continue immediately after the switch. This can also be used elsewhere in X++
coding. The default case executes if the result of the expression does not match
any of the cases. Using the default case is optional.
2-13
grade =
message
break;
grade =
message
break;
grade =
message
break;
grade =
message
break;
grade =
message
"A";
= "Excellent";
"B";
= "Good";
"C";
= "Average";
"D";
= "Poor";
"Failed!";
= "You need to study more!" ;
This example produces the same result as the previous example, but uses if...else
statements instead of the case statement. Note the number of lines of code and
the ease of reading the code in each case.
int score = 80;
str grade;
if (score == 90)
{
grade = "A";
}
else
{
if (score == 80)
{
grade = "B";
}
else
{
if (score == 70)
{
grade = "C";
}
else
{
2-14
if (score == 60)
{
grade = "D";
}
else
{
grade = "Failed!";
}
As illustrated on the previous page, the switch statement simplifies the code and
makes the program flow more efficiently. Another advantage of using a switch
statement is allocating multiple results of the expression to one outcome or case.
The following example shows the use of multiple expressions in a switch
statement.
str color = "red";
str colortype;
switch (color)
{
case "red", "yellow", "blue" :
colortype = "Primary Color";
break;
case "purple", "green", "orange" :
colortype = "Secondary Color";
break;
default : colortype ="Neither Primary or Secondary";
}
2-15
color = "red";
colortype;
if ((color =="red")
|| (color =="yellow")
|| (color =="blue"))
{
colortype ="Primary Color";
}
else
{
if ((color =="purple")
|| (color =="green"')
|| (color =="orange"))
{
colortype ="Secondary Color";
}
else
{
colortype ="Neither Primary or Secondary color"
}
Loops
Repetitive statements, also known as loops, conditionally control data input and
output. There are three main loops in X++:
While loop
Do while loop
For statement
While
The while loop evaluates a condition and executes statements, depending on
whether the condition is satisfied. The loop continues to check the condition, and
as long as the condition is true, it continues to execute the statements. As soon as
the condition becomes false, the statement is exited. The syntax is as follows:
while (condition)
{
//statement;
}
2-16
Result:
1
2
3
4
5
HINT: The previous example uses the counter++ which can increment any
integer by 1. You can also set the increment to any value by using the
variable+=<# to increment> syntax. For example:
counter+=2; //This increments the counter variable by two every time.
Notice the condition is evaluated before the statements are executed. In the
previous example, it means that if the counter variable is greater than five when it
reaches this loop, it does not execute. Therefore, a while statement can be
executed zero or more times.
Do...while
The function of a do while statement is almost identical to the while statement.
The main difference is that the condition is evaluated after the statement
executes. The effect is that the loop always runs at least one time. The following
is the syntax for a do while statement:
do
{
//statement;
}
while (condition);
2-17
For loops are frequently used when navigating through an array. The following
is an example of how to use a for loop to print each element of a string array
called 'abc' that contains 10 strings:
for (counter = 1; counter <= 10; counter++)
{
print abc[counter];
}
A for loop and a while loop perform the same function. However, the for loop is
more condensed in structure. In a while loop, if the counter variable is not
incremented, an infinite loop can occur. This is not possible with a for loop
because you receive a syntax error if that part of the condition is not qualified.
2-18
Result:
1
2
3
8
9
10
2-19
Challenge Yourself!
1. Create a job that prints the multiplication table for 2 using a while
statement.
2. Have the list contain all even numbers up to 50.
3. Use a 'counter' variable to increment every time.
Step by Step
1.
2.
3.
4.
int counter = 1;
while (counter <= 25)
{
print counter * 2;
counter++;
}
pause;
2-20
Challenge Yourself!
1. Create a job that prints the multiplication table for 2 using a
do...while statement.
2. Have the list contain all even numbers up to 50.
3. Use a 'counter' variable to increment every time.
Step by Step
1.
2.
3.
4.
int counter = 1;
do
{
print counter * 2;
counter++;
}
while(counter <= 25);
pause;
2-21
Challenge Yourself!
1. Create a job that prints the multiplication table for 2 using a for
statement.
2. Have the list contain all even numbers up to 50.
3. Use a 'counter' variable to increment every time.
Step by Step
1.
2.
3.
4.
int counter;
for(counter = 1; counter <= 25; counter++)
{
print counter * 2;
}
pause;
2-22
Built-in Functions
Microsoft Dynamics AX contains many built-in functions to help in X++
development. These functions perform mathematical operations, convert data
types, return system values, and so on.
Built-in functions can be used anywhere in X++ code. These functions can be
typed manually or accessed by using the context (right-click) menu in the Code
Editor and selecting List Built-in Functions, or by pressing Shift+F4.
To use a function, click it. The function is placed in the code where the pointer is
located.
This example uses a function that returns a subset of a string variable. The syntax
for the function is as follows:
str subStr(str text, int position, int number);
The following table describes the components of the function:
Component
Description
str substr
str text
int position
int number
str letters;
letters = "ABCDEFG";
print subStr(letters, 2, 4);
print subStr(letters, 5, -3);
pause;
2-23
Communication Tools
Communicating with the end-user is important and critical for an application to
run correctly. The main types of communication include:
Forms and reports used for input and output of larger amounts of
data.
This lesson discusses how to use the print statement, create dialog boxes and
Infologs. Forms and reports are covered in more detail in the next development
course.
Print...Pause
When the compiler reaches a print statement, it returns whatever value or
variable value that immediately follows the print syntax. The following is an
example of the print statement:
print "This is a test message.";
pause;
The pause line freezes the output text on the screen after the code runs so that it
is easier to read. The pause statement causes a message box to pop up. It lets the
user continue or end code execution.
The print statement should not be used within the actual application business
logic. It is used only as a programmers tool to display data or values while
developing and debugging code.
Boxes
Boxes display brief messages to application users. There are many box types and
each has their own box method.
Methods in the box class take the following parameters:
Help text
The following is an example of an information box and how the parameters are
used:
Box::info("Main Text", "Title", "This is the help text");
2-24
The string "This is the help text" appears in the status bar at the bottom of the
screen.
The box class contains many methods that create different types of boxes. These
methods can be viewed in the AOT under Classes > Box. Many boxes only
provide output to the user, whereas other boxes accept input from the user,
depending on which button the user clicks.
Some of the more commonly used boxes are discussed in this lesson.
The warning box is used to display warning messages. The following example
shows how the warning message is used:
Box::warning("This is a warning message.", "Title text",
"Help text");
2-25
The box function returns a value of the enum DialogButton that can be used to
determine which option the user chooses.
Infolog
The Infolog is the most common method used to communicate with the user
about how a process is executed. Boxes can output a message to a user. However,
sometimes multiple messages are generated during processing. Infologs are more
suited to handle multiple messages; there are three types of messages:
2-26
Message type
Description
Information
Warning
Error
2-27
The Infolog system is used mostly to display multiple messages at the same time,
in tree form. The following figure shows an example of multiple messages in tree
form.
The setPrefix() function sets the label for the heading of the Infolog tree. In the
example, it specifies the label, 'Infolog Tree'. The following example shows the
syntax of this method:
setPrefix("Infolog Tree");
2-28
BEST PRACTICE: Use labels when possible for the text of these Infolog
messages and the SetPrefix function.
Dialog Boxes
Dialog boxes are a simplified type of form in Microsoft Dynamics AX, and they
are generated from the Dialog class. They create a dialog with the user where the
user can input values. Dialog boxes are not used in complex scenarios; forms are
the preferred method in complex situations. All dialog boxes have a standardized
form that contains an OK/Cancel option.
NOTE: The Dialog class described here is a different concept then the Dialog
Form Template available as a context-menu option on the Form node in the
AOT. This topic describes creating Dialog forms using X++ code.
2-29
The following example displays the dialog box and prints the value entered to the
screen.
static void Simple_Dialog(Args _args)
{
dialog
dialog;
dialogGroup dialogGroup;
dialogField dialogField;
dialog
= new Dialog("Simple Dialog");
dialogGroup = dialog.addGroup("Customer");
dialogField =
dialog.addField(extendedTypeStr(custAccount));
if (dialog.run())
{
print dialogField.value();
pause;
}
}
2-30
Challenge Yourself!
Create a new job in the AOT that contains a box where the user can decide to
continue or cancel.
1.
2.
3.
Step by Step
4.
5.
6.
7.
DialogButton dialogButton;
dialogButton = Box::yesNoCancel("Do you want to continue?",
DialogButton::Yes, "Question", "Bottom help text");
2-31
Challenge Yourself!
Create a tree of Infolog messages using a job in Microsoft Dynamics AX. The
tree should have a root node which states: "This is the Infolog Tree lab." The tree
should contain at least one of each type of message (Info, Warning and Error).
Step by Step
1.
2.
3.
4.
2-32
Challenge Yourself!
Create a dialog box that lets the user select a customer account number and a
sales order ID. Once the user clicks OK, show the values of each selection using
the Infolog.
Step by Step
1.
2.
3.
4.
Dialog
DialogGroup
DialogField
DialogField
dialog;
dialogGroup;
dialogFieldCustAccount;
dialogFieldSalesId;
2-33
Challenge Yourself!
Create a dialog for the user that determines, based on input, what appears on a
screen.
1. Create a new job in the AOT and declare and initialize a
DialogButton object variable and a container data type variable.
2. The three elements of the container are your first name, last name,
and birth date.
3. The Dialog button should be a Yes No box with the text "Do you
want to display your birth date?" with Yes being the default button.
4. If Yes is clicked - first name, last name, and birth date appear.
5. If No is clicked - only first and last name appear.
Step by Step
1.
2.
3.
4.
DialogButton
container
dialogButton;
nameAndDOB;
nameAndDOB = ["John","Doe",mkDate(28,9,71)];
dialogButton = Box::yesNo("Do you want to see the
birthdate?", DialogButton::Yes);
if (dialogButton == DialogButton::Yes)
{
Box::info(strFmt("%1 %2, %3", conPeek(nameAndDOB,1),
conPeek(nameAndDOB,2), date2str(conPeek(nameAndDOB,3),-1,1,-1,-1,-1,-1)));
}
else
{
Box::info(strFmt("%1 %2",conPeek(nameAndDOB,1),
conPeek(nameAndDOB,2)));
}
2-34
2-35
Summary
This course introduced three conditional statements and three repetitive
statements. Depending on whether the conditions are true, different commands
can be executed.
This course also showed how the built-in functions can help when writing X++
code to perform mathematical operations, convert data types and so on.
Additionally, to communicate with the end-user, this course showed how to use
the Print Statement, the Box Class, the Infolog System and the Dialog Box.
2-36
2. Which arithmetic operator would you use to output the remainder when
dividing 83 by 10 and what would the syntax be?
2-37
2-38
2.
3.
2-39
Solutions
Test Your Knowledge
1. What are the three primary types of conditional statements in X++?
MODEL ANSWER:
A. If Statement: This statement checks whether a condition is true or false.
If the statement is true, all the code in the braces '{}' is executed. There are
other versions of the If statement including an Ifelse, or a nested If
statement.
B. Switch Statement: This is a multi-branch control statement that defines a
condition and whose result determines the code that is executed. A switch
statement has multiple cases and for each case there are listed statements that
will be executed if that case satisfies the condition.
C. Ternary Operator: This control structure acts as an Ifelse statement. It
is primarily used because it is much more convenient to code than writing out
an Ifelse statement.
2. Which arithmetic operator would you use to output the remainder when
dividing 83 by 10 and what would the syntax be?
MODEL ANSWER:
MOD
83 Mod 10
3. What is the main difference between using a while statement and a
do...while statement?
MODEL ANSWER:
The main difference between a While statement and a Do...While statement
is that the condition is evaluated after the statements are executed in a
Do...While statement. Therefore a Do...While statement always runs at least
one time.
2-40
2-41
2-42
Introduction
A class is a software construct that defines the data (state) and methods
(behavior) of the specific concrete objects that are subsequently constructed from
that class.
It can be thought of as a blueprint for different instances of an object. It is
possible for multiple instances of the object to exist, and for each instance to hold
different values in its variables, and therefore to run differently.
Scenario
Isaac, the systems developer, has been asked to print a name to the screen. He
would like to try some different ways of doing this in code.
3-1
Classes
A class is constructed of member which can be variables or methods.
Variables are used to store the data for the class. They are specific to
an object; every object instantiated from the class declaration has its
own copy of the variable. Such variables are known as instance
variables.
Methods are the functions that operate on the data and define the
object's behavior. They are often declared to operate on the instance
variables of the class, and are known as instance methods.
A class is not an object. A class is an outline that defines how an object behaves
when the object is created from the class. Obtain concrete objects by instantiating
a previously defined class. Many objects can be instantiated from one class
definition.
Declaration of Classes
A class contains the properties, methods, and variables needed by the object.
Create a class by right-clicking the Classes node of the AOT and selecting New
Class, or by using the Class Wizard at Tools > Development Tools > Wizards.
A class always contains a classDeclaration node and two methods:
New(): This method instantiates a new object. You can also assign
values to object variables using this method.
Finalize(): This method terminates the object. It is never used within
the application and exists only for convention.
3-2
3-3
Challenge Yourself!
Create a new class that has one class variable that can hold your name. Create a
method that will set the value of the variable to be your name. Create another
new method that can print the value of the variable.
Step by Step
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
3-4
Modifiers
There are three modifiers available.
When a new method is created, the default modifier of the method is Private.
3-5
Challenge Yourself!
Change the modifier of each method to Public
Step by Step
1.
2.
3.
4.
5.
3-6
Inheritance
Inheritance is a concept where one class can inherit all the methods and variables
from another class. A child class inherits the methods of the parent class.
Additional methods can be applied to the child class, and inherited methods can
be overridden. This means the child class is not always identical to the parent
class.
The advantage of inheritance in object-oriented programming is that code can be
written one time and be reused many times.
NOTE: In X++ one class can extend another class, and one extended data type
can extend another.
Extend a Class
To have a class extend another class, modify the classDeclaration code of the
child class by adding extends and then the parent class, shown as follows:
class Child extends Parent
{
}
3-7
Can have methods and variables that do not exist in the Parent.
Can have methods from the Parent that are overridden or altered, in
the Child.
Objects
Objects are created at run-time using the classes defined in the AOT. To create
an object from a class, the class has to be instantiated.
Instantiating a Class
Object instance methods can only be used when an object is instantiated from a
specific class. To instantiate a class, is to create a new instance of it. You can
have multiple instances of a class, meaning, you can have the same code running
multiple times. Think of how in Microsoft Word, you can have two documents
open at the same time. They are both running the same program, but are two
separate instances of that program.
When you create a new object, call the new() method to execute the code when
the object is instantiated.
3-8
myClass;
;
myClass = new myClass();
myClass.myMethod();
The objective of following lab is to instantiate a class, and then execute a method
from that class.
3-9
Challenge Yourself!
Create a job that instantiates the class written in the previous lab, and executes
the two methods you created.
Step by Step
1.
2.
3.
4.
PrintMyName
PrintMyName;
3-10
Scopes in X++
Instance variables, declared in class declarations, can be accessed from any
methods in the class, and from methods in sub-classes that extend the class.
Local variables can be accessed only in the method in which they are defined.
public class ANewClass
{
int
abc;
}
private void aNewMethod()
{
int
xyz;
_parm1, int
_parm2
{
}
3-11
Replace the void with an Extended Data Type to specify the type of
data to receive from the method. Any kind of data can be returned
including table buffers and class objects.
myTxt = "Text";
return myTxt;
}
3-12
Challenge Yourself!
Modify your class to print your name, by allowing the method that sets the name
variable to accept a parameter and set the name variable from that parameter.
You will also need to modify the job so that you pass your name into the method
when you run it.
Step by Step
1.
2.
3.
4.
5.
6.
7.
8.
myName = _myName;
PrintMyName.setMyName("Isaac");
PrintMyName.printMyName();
pause;
3-13
3-14
Challenge Yourself!
Add a method named run to the class that prints your name. From this method
call the two methods that set and print the value. Modify the job that executes the
class to call the new run method.
Step by Step
1.
2.
3.
4.
5.
6.
7.
8.
this.setMyName("Isaac");
this.printMyName();
3-15
Method Types
There are many different types of methods. Some of the more common types are
discussed as follows.
Static Methods
Static methods are attached to a class. However, they do not need that class to be
instantiated to execute that method. They are not within the scope of the class, so
any class variables are not available in a static method.
Static methods are declared static by using the Static method modifier.
Static methods are called using the class name followed by two colons (::) and
then the methods name.
The following example shows a static method declaration, and a call to that static
method.
static public void myStaticMethod()
{
}
myClass::myStaticMethod()
Main Method
The main method is a static method that can be used to call a constuctor. It is
special because its name is required to be "main". It is used by the system when
the class is run directly from a menu item and it takes a parameter of type args.
Args is a class that is used to pass parameters between objects, for instance,
various parameters can be set on the properties on a menu item. When the menu
item calls a class, the args class containing those property values is passed to the
main method using the args parameter.
3-16
inventTable
The first line in this code is a comment to the compiler indicating that a Best
Practice deviation is considered and evaluated as reasonable. The deviation is
because a display method is able to return any data from any table or field in the
system, and so the security implications should be considered.
Accessor Methods
Accessor methods enable other elements to set or get the values of variables in a
class and it is common that they do both.
The following example accepts a value as a parameter, sets a class variable to
this value and then returns the value. The parameter has a default value set to the
class variable value, so if the method is called without a parameter, it returns the
value of the class variable. If it is called with a value in the parameter, then the
class variable is set to this value.
public str myName(str
_myName = myName)
{
myName = _myName;
3-17
Tables as Classes
A table can be considered an independent class used to address fields or methods
defined on that table. In fact when a table is instantiated, it is done so with the
system class called xRecord. This class contains methods called when
committing record changes to the database and some other system methods that
operate on records.
Differences between tables and classes include the following:
Table Code
The following example illustrates how table code differs from code for a class.
str text;
text = CustTable.name;
print CustTable.name;
referred to directly
Eventing
The eventing feature lets the user use a publisher and subscriber model when
modifying Microsoft Dynamics AX. Events can be modeled in the AOT or be
used as a programming construct and can be handled in either X++ code or in
managed code.
NOTE: Modeled events are only available on classes and not tables or forms.
3-18
Eventing Terminology
Microsoft Dynamics AX events are modeled after the .NET event concepts and
introduce the following new terminology:
Term
Definition
Producer
The producer is the logic that contains the code that causes a
change. This means that it is the entity that emits events.
Consumer
Event
Event
Payload
Delegate
Event Handlers
Event handlers are methods that are called when the delegate is called, directly
through code (for the coded events) or from the environment (in the modeled
events). The relationship between the delegate and the handlers can be
maintained in code or in the AOT.
The X++ language now features the delegate keyword. When program conditions
meet the programmer's criteria for the event, the X++ code can call the delegate,
and that causes the delegate to call all the event handler methods that are added
to the delegate.
To create a delegate, right-click the class and select New->Delegate.
3-19
3-20
ret = _args.getReturnValue();
_args.setReturnValue(ret && dayOfWk(systemDateGet()) ==
WeekDays::Monday);
}
3-21
Challenge Yourself!
Create a class that has two class variables that are set using accessor methods.
These variables are to hold 2 numbers. Add four methods that will add, subtract
multiply and divide these 2 numbers, and then return the calculated value. Write
a job to test your class.
Step by Step
1. Create a new class
2. Add the following methods and code to the class.
public class Calculator
{
real
value1;
real
value2;
}
3-22
_value1 = value1)
_value2 = value2)
3-23
Summary
This course introduced objects and classes within the Microsoft Dynamics AX
X++ development, including how to declare classes and instantiate objects.
Additionally, you worked with variables using simple and extended data types,
and used events to reduce the possibility of code merging during upgrades when
modifying code.
3-24
2. What is the reserved word in a function that is used when the method does
NOT return a value?
3-25
2.
3.
3-26
Solutions
Test Your Knowledge
1. Name two different types of methods that can be created in X++ and describe
their differences.
MODEL ANSWER:
Object methods can only be activated by an object that has been instantiated
from the specific class. An object method is called as follows:
ref_name.testmethod();
Class methods can be attached directly to a class and do not need an
instantiated object to work. Use the 'static' modifier when you create class
methods. A class method is called as follows:
Class_name::testmethod();
2. What is the reserved word in a function that is used when the method does
NOT return a value?
MODEL ANSWER:
void
3. To execute a class's methods from a menu item, modify the __________
method.
MODEL ANSWER:
Main
3-27
3-28
Introduction
This course will show how to develop modifications that interact with the
database. This functionality is used frequently so it is an important part of
learning how to develop using X++.
Scenario
Isaac, the Systems Developer, is writing a program that requires data to be read
from the database. Additionally the data must also be updated and written back to
the database. To do this, Isaac must use the select statement and queries so that
the data can be retrieved correctly and efficiently, while ensuring the data
maintains its integrity.
4-1
Retrieving Data
Microsoft Dynamics AX 2012 accesses data using select statements and queries.
This section focuses on select statements.
Table Buffers
A table buffer stores complete records in a variable. A table buffer is declared
like a variable the table name is specified in the declaration. It is required when
you use select commands to retrieve records and is declared before the select
statement.
The following code shows a table buffer declaration:
CustTable
CustTable
When a select statement result is assigned to a table buffer, the variable can be
considered a subset of data from that table depending on the criteria of the
search.
Select Statements
Select statements are used to retrieve data from a database. The pure select
statement returns records to a table buffer. The following example shows code
that selects a record from the Customer table where the customer account number
is 1103 and also prints the customer's address. Use the legal entity CEU.
CustTable
custTable;
select custTable
where custTable.AccountNum == "1103";
print "This is the credit limit of Customer " +
custTable.AccountNum + ": " + num2str(custTable.CreditMax,1,-1,-1,-1);
pause;
4-2
Keyword
Description
Find Options
reverse
firstFast
firstOnly
forUpdate
noFetch
sum
avg
minof
Aggregate
4-3
Sorting
Options
Direction
Index Clause
Join Clause
Keyword
Description
maxof
count
order by
group by
asc
desc
index
index hint
exists
notexists
outer
4-4
Field Lists
To perform a lookup on specific fields in a table, use a field list in the select
statement. For example, to return only the name of a customer whose account
number is '4000' use a field select statement.
CustTable
custTable;
4-5
Sorting Options
You can sort data retrieved from the database in many ways. This includes:
To use an index on a select statement, use the keyword index followed by the
name of the index:
CustTable custTable;
4-6
Order By
If an index does not exist on a table, you can create a temporary index using an
order by clause in the select statement. This lets you specify one or more table
fields to sort the result. By default, records are sorted in ascending order. To sort
in descending order, use the keyword desc after the order by field. The following
example shows how to use the order by clause in descending order.
CustTable custTable;
Group By
Use the group by clause to group selected records by field. The following
example shows how to display the number of sales orders by customer group
from the Sales table.
SalesTable salesTable;
The output would be a list of customer groups with the total number of sales
orders that exist for customers in that group. The count() function counts the total
number of records and places the result in the field specified in the brackets.
4-7
Use to
inner
outer
exists
notexists
Not only is there additional syntax needed to specify what table is being joined,
but the where clause also has to be modified. Within the where clause, the two
fields that relate the tables must be specified in addition to any other conditions
of the search. When using an inner or outer join the table buffers of both tables
are populated with the retrieved records. An exists or not exists join only
populates the first table.
custTable;
CustTrans
custTrans;
4-8
Cross-company Support
Microsoft Dynamics AX can have multiple companies in one data base. It is a
common requirement to retrieve data from many or all of these companies in a
function. This can be achieved in one select statement by using the
crossCompany keyword. If just the keyword is used, it will search all companies.
You can also specify a container that defines which companies to search.
container
custTable
4-9
Challenge Yourself!
Create a job that will retrieve all customer accounts and print the account number
and name. The list should be sorted by name. Note that the customer name is not
stored in the customer table; it is stored in the DirPartyTable, which is related to
the customer table through the CustTable.Party field and the
DirPartyTable.RecId field.
Step by Step
1. Open the AOT.
2. Create a new job
3. Add the following code.
CustTable
CustTable;
DirPartyTable DirPartyTable;
while select DirPartyTable order Name
join CustTable
{
4-10
Data Manipulation
Data manipulation in X++ refers to interactively using SQL commands. These
commands include insert, update and delete. These are required to modify the
data in the database. When values are assigned to a table buffer, the data is not
updated in the database.
Insert
To create new records in the database, use the insert() method on the table. The
data is first set on the table buffer by assigning values to fields. The data is then
committed to the database by calling the insert() method.
custTable
custTable;
custTable.accountNum = "1234";
custTable.Currency = "USD";
custTable.insert();
Insert_Recordset
Insert_Recordset copies data from one or more tables in one call to the database.
This is much faster than selecting records individually and then inserting the new
records in to the table.
In the following example, Isaac has been asked to copy all Employee records to
the vendor table for the purposes of being able to pay expenses to the employee.
Only specific information, such as name and address needs to be copied.
VendTable vendTable;
HcmWorker hcmWorker;
Insert_RecordSet VendTable (accountnum, Party)
select PersonnelNumber, person from HcmWorker;
4-11
ttsbegin;
while select forupdate salesTable
where salesTable.CustAccount =="2001"
{
salesTable.SalesName ="New Enterprises";
salesTable.update();
}
ttscommit;
NOTE: The use of ttsbegin and ttscommit. This indicates the start and end of a
transaction. This topic will be discussed at the end of this section.
4-12
update_recordset salesTable
setting salesName ="New Enterprises"
where salesTable.custAccount =="2001";
NOTE: You do not have to use the ttsbegin; or ttscommit; when you use the
update_recordset command, however it is recommended to consider using a
transaction when making other updates to the database at the same time.
Delete
The delete command deletes a complete record from the database that meets the
condition of the select statement. The syntax of the delete command resembles
update and insert. This example deletes a row in the database with a customer
account of '2032'.
CustTable custTable;
ttsbegin;
Select forUpdate custTable
where custTable.accountnum =="2032";
custTable.delete();
ttscommit;
4-13
delete_from custTable
where custTable.Currency == "ABC";
Nested ttsbegins and ttscommits are ignored in that a lock is held until the last
ttscommit is reached. However the system does keep track of the tts level. Each
time a ttsbegin is called, the tts level increases by one. Every ttscommit decreases
the level by one.
4-14
Challenge Yourself!
Write a job that will find all items in the Television item group, and set the price
tolerance group to 2 percent.
Step by Step
1. Open the AOT.
2. Create a new job.
3. Add the following code.
InventTable InventTable;
InventItemGroupItem inventItemGroupItem;
ttsbegin;
while select forupdate InventTable
exists join inventItemGroupItem
where inventItemGroupItem.ItemId == InventTable.ItemId
&& inventItemGroupItem.ItemGroupId == 'Television'
{
InventTable.ItemPriceToleranceGroupId ="2%";
InventTable.update();
}
ttscommit;
InventTable InventTable;
InventItemGroupItem inventItemGroupItem;
ttsbegin;
while select forupdate InventTable
4-15
4-16
Queries
A query is an application object in the AOT. A query performs the same function
as the select statements. It is a better option because it allows for more flexible
user interaction when defining which records are to be retrieved. Queries provide
more flexibility, especially when sorting and specifying ranges.
The following figure shows an expanded query in the AOT:
With this example, the CustTable query has three data sources, two of which
have defined sorting and ranges. This section discusses methods within queries
and how to construct a simple query using X++.
Query()
QueryRun()
4-17
QueryBuildDataSource
QueryBuildRange
QueryBuildDataSource
Data sources are what queries are built upon. They are arranged in a hierarchy
and define the sequence in which records are fetched from tables assigned to the
data source. The following example adds a data source to a query using X++:
Query
QueryBuildDataSource
qbds =
query.addDataSource(TableNum(SalesTable));
Notice that the data source is an object, but the query object, 'query', requires a
method to add this data source to the query and assign the SalesTable to the data
source.
4-18
QueryBuildDataSource qbds =
query.addDataSource(TableNum(SalesTable));
QueryBuildRange qbr =
qbds.addRange(FieldNum(SalesTable,CustAccount));
query;
queryRun;
qbds;
qbr;
SalesTable;
4-19
print SalesTable.SalesId;
pause;
4-20
Challenge Yourself!
Write a job that will produce a list of vendors. The job should build a query using
X++, and the query should have a range that limits the list to vendors in vendor
group "50", and sort by account number.
Add code that will allow the user to modify the query ranges at run time.
Step by Step
1.
2.
3.
4.
Query
QueryRun
QueryBuildDataSource
QueryBuildRange
vendTable
query;
queryRun;
qbds;
qbr;
vendTable;
4-21
Summary
This course showed how to search for data using the select command and how to
create criteria to specify which records will be selected. This course also showed
how these records can be updated or deleted and how to insert new records.
Additionally, this course showed how to build a more user-friendly search using
queries to specify the search criteria.
4-22
4. What are the three keywords associated with the Transaction Tracking
System and what is their significance?
4-23
2.
3.
4-24
Solutions
Test Your Knowledge
1. What is wrong with the following line of code? Rewrite it so that it compiles
correctly: select count(vendTable.AccountNum);
2.
MODEL ANSWER:
You are only supposed to specify a field name as a parameter to the keyword
COUNT and then add a from clause after this function. The following shows
the code written correctly:
Select COUNT(accountnum) from vt;
3. What are three ways to sort data on a select statement?
MODEL ANSWER:
a. Using existing indexes on the tables
b. The order by clause in a select statement that will return records in
ascending order by default and if specified, can return them in descending
order.
c. The group by clause in a select statement will group records by a selected
field.
4. What are the three keywords associated with the Transaction Tracking
System and what is their significance?
MODEL ANSWER:
ttsBegin: You mark the beginning of the transaction with ttsbegin.
ttsCommit: You mark the successful end of a transaction with a ttscommit.
This ensures the transaction is performed the way it is supposed to upon
completion.
ttsAbort: This can be used as an exception to make a transaction be aborted
and rolled back to its state before the ttsbegin. You insert a ttsAbort wherever
you want this to occur.
4-25
4-26
Introduction
When code is running, exceptions can occur because of user input, setup, data,
code, or installation problems. Users need a clear indication of when exceptions
occur so that they can resolve the problem or report it to an administrator or
systems developer, who can investigate what went wrong.
Therefore, a good programming practice involves handling exceptions
throughout the program, and consistently reporting. When an exception occurs, it
is known as an exception being thrown. The ability of the program to know that
an exception has occurred and to react to it is known as catching the exception.
Within Microsoft Dynamics AX2012, X++ has a built-in mechanism to help with
exception handling. This feature allows developers to catch exceptions during
program execution and control how the program reacts to the exception.
Scenario
Isaac, the Systems Developer, needs to improve his code so that any errors that
occur during the execution of the program, whether because of user input, data,
his own code, or any other reason, are reported to the user, and that actions can
occur to correct the error.
5-1
Exceptions
An exception is a situation where the flow of a program's execution is
interrupted. Examples of exceptions include the following:
When these exceptions occur, the program must handle them. For example, if the
user requests a file that does not exist, the program might have to catch the
exception and create a new file.
There are many types of exception thrown, depending on what caused the
exception. A majority of exception types are determined by the kernel and are not
usually thrown by application code. However, all exception types can be caught,
and it is the developer's responsibility to decide which exceptions must be
handled. The exception type is identified using the system enum Exception.
Because it is a system enum, it cannot be modified. Therefore, users cannot add
new exception types.
5-2
Exception
Description
info
warning
deadlock
error
internal
break
DDEerror
sequence
numeric
CLRError
CodeAccessSecurity
Description
UpdateConflict
UpdateConflict
NotRecovered
custTable.AccountNum = "0001";
custTable.CustGroup = "50";
custTable.insert();
}
catch (Exception::Error)
{
error("There was an error while inserting the
record.");
}
5-3
There are then two catch statements. The first catches an error exception. The
error may be caused, for example, during the insert() method. The second catch
statement catches a deadlock exception. The database throws this exception when
it recognizes that a deadlock has occurred.
When a deadlock exception is thrown, all locks on tables that this process holds
are released. This could allow the other process or processes that are also
deadlocked to continue. By calling a retry, the process can attempt to update the
record again and may now be able to be completed. It is a best practice that a
retry uses a counter so that the number of retries can be controlled, and a user
does not become stuck in a loop.
Throwing Exceptions
A Throw statement is used to throw an error that can be caught by a Catch
statement. The Throw statement explicitly declares what type of exception is
thrown to the Catch list. If the Try statement does not include a Catch list, the
exception moves to the next Try statement.
In the following example, the code throws an error exception and at the same
time, an error message is placed in the Infolog (this is a feature of the error
function in X++). When the error exception is caught, a second error message is
placed in the Infolog. Both error messages are then reported to the user.
CustTable custTable;
try
{
select custTable
where custTable.accountNum == "1019";
if (!custTable.RecId)
throw error("Customer does not exist");
}
catch (exception::error)
{
error ("Process was aborted");
}
5-4
5-5
custTable;
custTable.AccountNum = "0002";
custTable.CustGroup = "20";
if (!custTable.validateWrite())
{
throw error("Record failed validation");
}
custTable.insert();
...
}
catch (Exception::UpdateConflict)
{
//the TTS level is automatically reduced by 1
if (appl.ttsLevel() == 0)
{
if (xSession::currentRetryCount() >= #RetryNum)
{
throw Exception::UpdateConflictNotRecovered;
}
else
5-6
retry;
}
else
{
throw Exception::UpdateConflict;
//thrown to the caller
}
}
If a conflict due to OCC occurs, the system throws the UpdateConflict exception
and it is caught by the catch statement.
The code checks the current TTS level. If it is not zero, in other words, it is still
in a TTS transaction, it throws another UpdateConflict exception to the next
Catch list of the next outer Try statement in scope. This continues until it is no
longer inside a TTS transaction. The developer must make sure that when the
code is retried, all data is refreshed.
As soon as the code is outside all transaction scope, the retry statement is called.
The Try statement is then re-executed. If the problem continues for as many
retries as is specified in the #RetryNum macro (system default is 5), then the
UpdateConflictNotRecovered exception is thrown. This means the whole
transaction is aborted and stops retrying.
5-7
Challenge Yourself!
Create a new class that will prompt the user for a new customer account number,
the name and all fields that are mandatory for the customer records. Use existing
methods to validate the record before inserting the record into the database. In the
case that not all data was entered, throw an error exception that has the error
message: "Please enter all required fields." The code to create the record must be
inside a try/catch statement. This must include a catch for the error exception
type and display the message: "An error occurred. Please try again.".
Step by Step
1. Create a new class.
2. Copy the following methods by copying the classDeclaration to the
classDeclaration, and creating the main and run methods.
3. Compile the code.
4. Test the code by right-clicking the class and selecting open.
5-8
dlgCust = dlg.addField(extendedTypeStr(CustVendAc),
"Customer account");
dlgGrp = dlg.addField(extendedTypeStr(CustGroupId));
dlgCur = dlg.addField(extendedTypeStr(CurrencyCode));
if (dlg.run())
{
try
{
custTable.AccountNum
custTable.CustGroup
custTable.Currency
fields.");
= dlgCust.value();
= dlgGrp.value();
= dlgCur.value();
if (!custTable.validateWrite())
{
throw error("Please enter all required
}
else
{
custTable.insert();
}
}
catch (Exception::Error)
{
error("An error occurred. Please try again");
}
_args)
custCreateCustomer = new
custCreateCustomer.run();
}
5-9
Summary
This course described how Exception Handling is controlled in Microsoft
Dynamics AX. This course discussed the exception handling framework and
what steps are necessary to communicate exceptions to the end-users. Labs and
demonstrations throughout provided an opportunity to practice using try and
catch statements, the main tools that are used to handle exceptions.
5-10
5-11
5-12
2.
3.
5-13
Solutions
Test Your Knowledge
1. What is the main idea behind exception handling in X++ programming?
MODEL ANSWER:
The main idea behind exception handling is to plan for potential pitfalls
within the program. These exceptions are inevitable and if the code can
expect some of these issues and have contingency plans for the code, the
flow of the program is more efficient.
2. What is the purpose of the try statement in a try...catch statement?
MODEL ANSWER:
The try statement defines a block of code that is included in the try...catch
statement. It is used as the point to which the logical program flow will
return should it encounter a retry command.
3. What is the function of a retry statement and when is it most useful?
MODEL ANSWER:
The retry statement is used to make the try statement execute one more time.
This statement is used more for simple locks that can be released while in the
catch statement. This enables the try statement to execute without an
exception the next time through. It is most beneficial for overcoming
database locks.
4. How is the throw statement related to the Transaction Tracking System (tts)?
MODEL ANSWER:
The throw statement automatically initiates a ttsAbort statement every time
that it is called. The throw statement will force the current transaction to roll
back to its original state before the ttsBegin statement.
5-14
Introduction
This chapter introduces some more advanced security features of Microsoft
Dynamics AX.
6-1
Permissions
The Development I in Microsoft Dynamics AX 2012 training course discussed
roles duties and privileges. These security levels cover access to single elements,
for example forms, and groups of elements needed to perform a duty. A
developer is responsible for defining more granular security levels by setting
access on tables and controls in a form, or by associating classes that perform an
action with a permission.
Form Permissions
Each form in the Application Object Tree (AOT) has a permissions node that
contains either four or five sub-nodes - Read, Update, Create, Delete and Correct.
Correct is only displayed if a table in the form has Date Effective data. Under
these nodes are four additional nodes - Controls, Tables, Server Methods and
Associated Forms.
When a table is added to a form data source, the table is automatically added to
the Tables node for each of the Permissions sub-nodes. Each of the nodes under
the Tables node has an EffectiveAccess property which sets what access is
allowed to the table
The EffectiveAccess property is automatically set based on properties on the data
source. If the data source property AllowDelete is set to No, the EffectiveAccess
property is set to Update. If the data source property AllowEdit is set to No, the
EffectiveAcces property is set to Read.
To set access for a control in the form, set the Securable property on the control
to Yes. The control can then be added to the Controls node under each of the
permissions nodes.
6-2
Security Policies
Security policies use Extensible Data Security (XDS).
Definitions
The following definitions are used in conjunction with XDS.
Constrained table: This table or tables hold the data filtered based on the policy.
Primary table: This table is used to determine how data is filtered. For example,
for a filter based on an employee, the primary table would be the HcmWorker
table.
Policy query: This query is used to return data from the primary table that is then
used to filter the data in the constrained table.
Context: This controls the circumstances under which the policy is applied.
There are two types of context:
6-3
6-4
6-5
The code asserted the appropriate permission on the call stack to use
the secured class.
The assert (the request to use the secured class) is executed in trusted
code and saved in the AOT.
Code Access Security covers the use of secured classes on the server tier only.
You do not need to modify or mitigate client-only invocations of secured classes.
Code Access Security must be implemented by the secured class owner and all
consumers of the secured class. The owner secures the secured class by
implementing a specific type of permission class and calling the demand()
method on that class. Each class consumer must explicitly request permission to
invoke a secured class by calling the assert() method on the permission class.
Application code will break unless both of these steps are completed.
NOTE: Code Access Security does not guarantee the validity of any data or
parameters passed to the secured class. Data validation is still the responsibility
of the consumer.
There are six groups of protected standard classes in Microsoft Dynamics AX
Code Access Security:
6-6
Direct SQL
File handling
Win32 Interop
Windows API
6-7
6-8
Summary
This chapter showed how to create XDS policies to secure data based on a
predefined filter, how to assert permission for a secured API and how to secure
code in display methods.
6-9
6-10
2.
3.
6-11
Solutions
Test Your Knowledge
1. How is the table that is filtered due to an XDS policy referred to?
( ) Primary
( ) Foreign
( ) Filtered
() Constrained
2. Which method in the ExecutePermission class tells the AOS the programmer
has investigated the possible security implications of the code that follows
the command and has assessed that it is safe?
( ) Allow
( ) Permit
() Assert
( ) Revert
6-12