If you are looking for a free X++ training April 2025 guide or reference PDF or similar well the closest you might get is this Dynamics Edge Microsoft Dynamics 365 Finance and Operations X++ developer training April 2025 overview. Please be advised that everything on this page include any X++ example code is on an as-is and reference only basis and should not be relied upon for any production or critical scenarios, we’re not responsible for any damage that may be caused if you do this.
When it comes to what is X++ training and all about how to use it, the pricetag of “free” will only take you so far. We don’t recommend you rely too much on this free D365FO training overview on X++ and instead, you should take our professional paid D365FO MB-500 training in March 2025, April 2025 and beyond or contact us for more information for additional options like custom X++ consulting or training options.
The below can seem like a press CTRL+P to get a free X++ pdf training guide of this page and you’re done, but careful: it’s more to get the idea of what X++ fundamentals you might want covered in our FinOps Dynamics 365 training or consulting for X++ developers. We’re not responsible if you make any mistake on your production environments because this page is not intended to be a substitute for a paid Dynamics Edge Dynamics 365 Finance and Operations training course for developers on X++. We also offer custom consulting / solutions packages and offerings in case you need training or consulting on very specific or advanced X++ scenarios including Power Platform integrations, in house integrations between Dynamics 365 FO and your other systems, custom scripts and more.
What is X++ programming language and what is X++ used for?
If you’ve ever asked about the X++ language you may be wondering what is X++ anyway and what is it even used for? X++ is an object-oriented, data-aware programming language which is usually used to develop your customizations for Microsoft Dynamics 365 Finance and Operations (D365FO) and also for its predecessor known as Dynamics AX. It was originally designed for ERP systems. It is combining traditional programming with your database manipulation capabilities all built in. When you use D365FO, all of your server-side business logic (your methods on forms, tables, classes and so forth) is actually written in X++.
Your development is usually done in an IDE called Visual Studio with your D365 Finance and Operations SDK integration. When it comes to X++ syntax and structure it may somewhat be familiar to C# and Java developers in that it somewhat sort of looks like C# or Java. Developers familiar with either of those languages may be able to pick X++ up more quickly.
Keep in mind though that if you start comparing C# development with X++ development or differences between X++ and C# you should know that X++ operates in what’s known as the Dynamics Application Object Tree (AOT). It includes an SQL-like query language integrated in for your database operations, and there’s a lot of other things that make X++ development quite different from standard C# development or .NET development. Your X++ code tends to be compiled to what’s called .NET CIL (Common Intermediate Language) and should be running directly on the server, which means you get more interoperability with your other dot net languages and frameworks.
Syntax Overview: X++ syntax uses a kind of C-style notation (curly braces for your blocks, semicolons to end statements). It also supports most of the standard constructs like your variable declarations, expressions, and your class definitions. X++ Comments can tend to be written as single-line // comment or even as multi-line /* comment */ but in general the better practice is to use //, even for your multi-line comments.
// This is a single-line comment in X++.
/* This is a
multi-line comment. */
Constants and Macros: Modern X++ (D365FO) can supports the const keyword in order to declare X++ constants which are constant values and which were introduced in AX7 version. For instance you may do: const int MAX_RETRY = 3;
which defines an integer constant. In some older AX versions, or when you are needing compile-time constants, Dynamics AX developers can use preprocessor macros. A macro tends to be defined with #define
and can be used in your code in some way like this:
#define.MaxRetry(3)
…
if (xSession::currentRetryCount() > #MaxRetry)
{
// ...
}
Macros tend to be evaluated at compile time. X++ macros provide you with directives quite similar to C/C++ (e.g. #if, #else, #endif) for your conditional compilation. Before AX7, X++ really had no const keyword, so #define was pretty much really the only way to represent your constants in your code. In modern D365FO you should prefer const or X++ readonly for maintainability by the way.
Attribute Classes: X++ supports your attributes (annotations) kind of similar to .NET attributes. X++ attribute classes are used for you to add your metadata to all of your classes, your methods, and even your fields. An attribute in X++ is actually a kind of class that extends the SysAttribute base class. You can create a brand new attribute by essentially defining a new class with extends SysAttribute (often with some Attribute suffix in the name). For example something like this:
public class SampleAttribute extends SysAttribute
{
// Constructor can accept literal parameters for metadata
public void new(str _param) { /* ... */ }
}
You can then decorate your classes or methods with using potentially C# reminiscent syntax like [Sample("Value")]
in the declaration. Your X++ compiler tends to treat these as a sort of metadata. This metadata can end up being retrieved by something called reflection during runtime. Attributes would be used generally in your D365FO for services (examples: [DataContract]
, [DataMember]
for serialization) and for your event handlers. They can provide you with such a powerful way to add your declarative information to code.
X++ language syntax on data types, operators, and statements in detail beyond this overview are not offered in this free training guide on X++ solutions for now. Further areas of exploration in custom training and X++ consulting can include X++ macros, directives for your advanced conditional compilation. More and deeper insight into creating and using your custom attributes. Dynamics 365 FO also introduced many language enhancements similar to how you might use C# including private/protected members, readonly, etc. which may also be worth exploring.
X++ Control Flow and Loops
When you consider X++ vs C# it may seem similar at first glance since X++ does use a lot of standard control flow structures quite similar to C/C# for you to know how to start making decisions and repeating actions from your code. Conditional statements can include if…else and your switch statements too (with case labels). Your X++ Loop constructs that are available in X++ include your for, while, and do…while loops. There’s also a specialized for…select for your database queries, which tends to be part of X++’s whole set of integrated SQL features. You can also use break and continue inside your loops, just like in other languages, in order to control your loop execution.
For Loop: Your X++ for loop syntax is almost identical to the C-style for-loops: for (initializer; condition; increment) { … }
. For example, the sample code below might fill an integer array and then print values:
int nums[10];
for (int i = 0; i < 10; i++)
{
nums[i] = i * 2;
info(int2str(nums[i]));
}
This loop might iterate 10 times, and the info(...)
statement might help with outputting each of the values. In a real environment, messages might go to the Infolog. Your for loop in X++ can work pretty well with any kind of numeric or iterator-based sequence. It tends to be quite useful for traversing your indexable collections (like arrays, containers).
X++ container means a composite data type that is usually 1 based rather than zero (0) based and contains a sequence of values in order usually primitives like str, int, real, boolean and similar. Containers in X++ are usually passed by value rather than reference. X++ statements or functions usually internally build a new container so they are immutable and they can be stored as database column created by AOT such as to store files or images as blobs as one example. conPeek conPoke and conIns are some example of functions, and += act as container insert operator for example.
While and Do…While Loops: An X++ while loop repeats as long as some Boolean condition is true. It checks the condition before each iteration. For example like this:
int count = 1;
while (count <= 5)
{
info(strFmt("Count = %1", count));
count++;
}
A do...while
loop checks the condition after it has been executing the loop body. An X++ do while loop should run at least once whether or not the condition was true initially.
int x = 0;
do
{
x = randomInt(); // hypothetical function
} while (x == 0);
Here the loop might keeps picking a random integer until it gets a value that is not zero.
Because it’s a do while loop, you should be able to change it to while x == 1 and it should still have run the do block at least once anyway.
Loop Control: The X++ continue statement tends to skip the remaining loop body and then jump right into the next iteration. X++ break is a statement that should exit the loop body entirely and continue with any statements that might be after the loop body. These work pretty much the same way in X++ as in C#. For example, in a for loop iterating 100 times, you could choose to continue if a value is not valid (to skip it) using continue statement. Or, you can break if you found what you were looking for, to stop early and break out of the loop. You can also consider exploring the for…select construct in X++ which tends to be used to loop through your table records (a really important part of X++’s data access pattern) and for that you can contact us for more information.
X++ Error handling and X++ Exceptions
Error handling in X++ is usually done using exceptions and follows a similar pattern to other high-level languages like C# for instance. You use try {...} catch {...}
blocks to capture your exceptions. Generally use a throw
statement to raise your error, and some optional finally
block for cleanup logic. X++ Try Catch can be used with an Exception enumeration with pre built in values like Exception::Error, Exception::Deadlock, Exception::UpdateConflict, and more for your common exception scenarios. It also supports throwing your .NET exceptions (like System.Exception or its subclasses) when you may need it.
try
{
// ... some database operation
throw Exception::Error; // manually throw a generic error
}
catch (Exception::Error)
{
error("An error occurred in processing."); // log to Infolog
}
In the above kind of example, the catch block is the one that catches the specific exception of type Exception::Error X++ while you can also have a more general catch (without specifying the exception) to catch any exception, sometimes this is known as a catch all block, the problem is it can be not the best idea. Best practice is to just usually handle expected exceptions explicitly as swallowing exceptions can lead to hard to track bugs.
Update Conflicts and Deadlocks: In a multiple user system like D365FO, you may encounter some database concurrency exceptions. An update conflict exception in D365FO can occur when your code has tried to update your record that has already been modified by someone else sometime since it was already read. In other words it was with an old or stale version of your data. A deadlock can tend to occur if two of your processes lock your resources in the opposite order. X++ can signal these by Exception::UpdateConflict and Exception::Deadlock enum values (or the corresponding exception classes). You usually catch these in the data write logic. A quite common pattern is to go ahead and catch these types of exceptions and then use the X++ retry statement to try your operation again after a brief pause.
try
{
ttsBegin;
// ... update database records
ttsCommit;
}
catch (Exception::Deadlock)
{
// On deadlock, automatically retry the transaction
retry;
}
catch (Exception::UpdateConflict)
{
// On update conflict, maybe refresh data and retry
info("Update conflict, will retry...");
retry;
}
The retry keyword in your catch block causes the control to jump back to the beginning of your associated try block. This can be quite useful for transient errors. In this case the transaction can be re-run, and this time hopefully succeeding on the second attempt. X++ tends to count how many retries have already occurred through xSession::currentRetryCount()
so you can actually break out after a certain number of attempts (and it’s a good idea to do that) to avoid an infinite loop situation. There are also enums like Exception::UpdateConflictNotRecovered which can be used to throw after max retries, if you may need it.
Working with Strings in X++
Strings in X++ can be usually represented by the str data type. Your X++ strings tend to support some normal operations like concatenation, like comparison, and searching. There is not really a distinct char type (a str of length 1 is usually used for single characters). Your string literals can usually be surrounded by double quotes (“Hello”) or even enclosed in single quotes (‘Hello’), with double quotes tending to be more common.
String Concatenation: To concatenate (join) strings, X++ string concatenation tends to use the + operator, as if you are using addition between numbers in math and just like C#.
str firstName = "John";
str lastName = "Doe";
str fullName = firstName + " " + lastName; // "John Doe"
The plus operator tends to concatenate your string operands together. If you might need to convert other data types to string with X++ concatenate string you can end up using some useful global functions like int2Str()
or the strFmt()
function for formatting your strings. X++ also allows you to leverage implicit conversion and for you to do that you can start using the print statement or info()
. An example is how some code like info("Value=" + num);
can call num2Str
behind the scenes.
Common String Functions: X++ provides you with many runtime functions for string manipulation. For example, a function like strLen(myStr)
gives you the length. Code like subStr(str, start, length)
extracts for you a substring. A code like strFind(str, substring, startPos, count)
can find a substring for you (returning an index), and so forth. These are quite similar to .NET’s string methods in many ways. X++ strings are actually 1-indexed when using some of these functions and note that X++ containers and arrays are 1-indexed as well rather than the zero based that you may be used to from some other programming languages. For more complex operations, one can also leverage your .NET classes (e.g., System.String
) by interop, since X++ can actually call .NET methods.
Splitting Strings: To split your string by a delimiter (for example, for splitting a comma-separated list), X++ can use the container type as an intermediary. With help of an X++ container for X++ split string by delimiter a container is actually a kind of lightweight collection that is able to hold a sequence of values for you of different kinds. The global function str2con(str _text, str _delimiter)
can split your string into a container of substrings and on a delimiter of your specification or choice.
str colors = "red,green,blue";
container parts = str2con(colors, ",");
// parts now contains ["red", "green", "blue"]
You can then access each part of your string with conPeek(parts, index)
(or convert the container to something else). Using str2con
converts each segment to the most appropriate type (for instance about how numeric substrings become int or real automatically). It is useful but something for you to be aware of. To treat all parts as strings, you might want to make sure that they contain non-numeric characters or use some other approach. There is also con2Str(container, delimiter)
for the reverse operation (like joining a container together into one string).
Example of Splitting: Suppose you read a line from your CSV file and want to get your individual fields:
str line = "Alice;30;Developer";
container fields = str2con(line, ";");
str name = conPeek(fields, 1); // "Alice"
str age = conPeek(fields, 2); // "30" (will actually be "30" as a string or int 30 depending on context)
str title = conPeek(fields, 3); // "Developer"
After splitting, you may have each piece of your data separated. In this example, some of your fields could contain three elements. This approach can be considered quite straightforward for many parsing tasks on the simpler side.
For further study inquire to Dynamics Edge for more details and you can consider more X++ string runtime functions like strCmp
, strLwr/strUpr
(to change the case), strRem
(remove your characters) and more. The use of your containers for string manipulation is quite unique to X++, so understanding your container functions (conPeek
, conLen
, etc.) can be quite useful. Know also that since X++ can actually interoperate with .NET, you might want to explore using the .NET string methods or the .NET System.Text
libraries for advanced text processing situations.
X++ Collections and X++ Iterators
X++ offers you collection classes (also known sometimes as foundation classes) for working with groups of your data. X++ collections or collection classes are sort of similar to some equivalent data structures in other languages. The main kind of collection classes in X++ are Array, List, Map, Set, and Struct. These classes live in the Microsoft.Dynamics.Ax.Xpp
namespace and are typically implemented in the kernel for performance.
- Array – An X++ Array is an object quite similar to X++’s built-in array type but more dynamic and can actually hold elements of any one specified type (including your objects and records).
- List – An X++ List is an ordered list of elements (sort of like a singly-linked list). It can support your sequential access and methods like
addStart
andaddEnd
. - Map – An X++ map tends to be a dictionary of key-value pairs. With map X++ D365FO each kind of entry can map a key to a particular value, so both the key and value can actually be any X++ type – and it’s usually specified at the time of map creation.
- Set – An X++ Set is an unordered collection of your unique values (no duplicates). It’s good for membership checks like
(mySet.in(value))
. - Struct – An X++ Struct is a kind of structure that may contain fields of multiple kinds. These are usually identified by name and it’s essentially a way to group values of different types together.
When you are instantiating these collections, you may usually specify the kind of elements they can contain. For example: List myList = new List(Types::String);
can create a list that holds strings. You usually cannot store objects of a wrong type into your typed collections. If you need a heterogeneous collection, you might want to instead use container or even a Struct. You may also want to instead specify the Types::Class/Types::AnyType
in older versions but note that in recent versions of Microsoft Dynamics 365 Finance and Operations X++ training we might make the important note that the AnyType specification is usually no longer allowed for new collection instances.
Iterating Collections: To loop through your collection contents, X++ provides you with something called enumerators and iterators. An X++ enumerator is obtained by using the getEnumerator()
method on a collection (like List, Map, Set) and ends up usually being used to read through your elements. An X++ iterator or X++ list iterator is actually obtained by getIterator()
or by creating a new iterator object (like MapIterator
) and can end up being used to both traverse and modify your collection even during iteration.
List myList = new List(Types::Integer);
myList.addEnd(10);
myList.addEnd(20);
myList.addEnd(30);
ListEnumerator en = myList.getEnumerator();
while (en.moveNext())
{
int value = en.current();
info(int2Str(value));
}
In this sort of example, ListEnumerator can be used to iterate through a List of your integers. The moveNext() method is the one that advances your enumerator to the next element (and is returning false when at the end). Here, current() is what retrieves the current element.
For a map:
Map map = new Map(Types::String, Types::Integer);
map.insert("Alice", 100);
map.insert("Bob", 95);
MapEnumerator mapEn = map.getEnumerator();
while (mapEn.moveNext())
{
str name = mapEn.currentKey();
int score = mapEn.currentValue();
info(strFmt("%1 -> %2", name, score));
}
Here we use the currentKey() and currentValue() on the MapEnumerator to get your key and value of each entry
Enumerator vs Iterator: So what’s the difference between X++ enumerator vs X++ iterator and when should you use one versus the other? What you should know about this is that under the hood, an X++ enumerator (from getEnumerator) tends to be a much safer, and even more efficient way to loop, especially when it comes to doing so in a read-only way. Enumerators in X++ are automatically created kind of on the same tier as a collection and so they may generally perform better in many scenarios with less code overhead. On the other hand when it comes to an X++ iterator (like MapIterator, SetIterator or ListIterator you can get one by getIterator()) that allows you to perform in-place modification – in other words, you can change what’s in it. You can use things like MapIterator.delete()
to remove your current element while you are iterating. You should be quite careful though because modifying a collection might invalidate any active enumerators (and the opposite as well). The best practice is to use X++ enumerators when you really just need to read through your elements, and to use X++ iterators if you are pretty sure you need to add or remove individual elements or change your collection in any other way during the actual loop itself. If the collection itself is changed outside of your loop, please be aware that the iterator or enumerator might become invalid and there may be some cases where it can happen.
Other Collection Features: All your collection classes have some useful methods (e.g., map.exists(key)
to check for a key, list.find(value)
to perhaps find an element and more). They also tend to override the toString()
method so you can have a readable representation (quite helpful in the debugger or Infolog for instance). X++ uses efficient C++ implementations in the back end for runtime, but the problem is that when running as .NET CIL, there may be slight differences (for example, certain iterator behaviors could be differemt when executed in CIL).
You may want to explore the Types system enum further (like Types::Class
, Types::Int
and more) which can be used to declare your collection element types.
Dynamics AX and D365FO System Components (Xpp Framework)
X++ runs on a quick optimized framework that integrates with your application’s runtime and the .NET environment. Key system components can include the base classes as well as the namespaces that make X++ work so well in D365FO. Two important pieces are the Microsoft.Dynamics.Ax.Xpp
namespace and the XppObjectBase
class:
Microsoft.Dynamics.Ax.Xpp Namespace: This namespace (in the assembly Microsoft.Dynamics.AX.Server.Core.dll) has the fundamental classes that support X++ runtime including the different collection classes (List, Map, etc.) and system types we discussed before. As well classes for X++ exceptions, for reflection, and even for runtime services. For example, classes like Global
(with many utility methods), Appl
(for application context), xSession
(for session information), and Infolog
tend to be part of the application’s global singletons (not sure what is global singleton or why it’s important? Ask us for more info). Things like XppPrePostArgs
, XppObjectBase
, etc., can tend to be part of the Xpp namespace. You do not generally need to reference this namespace explicitly in your X++ code (because these classes are usually available by default), but it can be helpful to know they exist and how they work for your interoperability and some advanced scenarios.
The Dynamics.AX.Application namespace is where AsyncTaskReskResult, AOSSessionInfo and more are located. The AOSSessionInfo class tends to be providing you with pertinent information about sessions for D365 Finance and Operations and the Application Object Server (AOS).
[Microsoft.Dynamics.Ax.Xpp.KernelClass]
[Microsoft.Dynamics.BusinessPlatform.SharedTypes.InternalUseOnly]
public class AOSSessionInfo : Microsoft.Dynamics.Ax.Xpp.XppObjectBase
Since it’s KernelClass and InternalUseOnly it is likely an internal kind of class that shouldn’t be directly manipulated by developers or by other applications. An AOS session D365 usually has to do with the three tiers, and usually the 2nd tier of executing your business logic. Dynamics 365 Finance & Operations (D365FO) tends to have a three-tier architecture. This is known as the Intelligent Client (the first tier), then the Application Object Server (which is AOS, second tier), and then the Database Server (third tier). You connect to AOS and are assigned a Session which has to do with authentication and business logic. To understand more about three tiers, inquire to Dynamics Edge for more information.
XppObjectBase: This is considered the abstract root class for all your X++ classes (sort of comparable to System.Object
in .NET). Every class that you define in X++ implicitly extends X++ XppObjectBase (in case that no other base class is specified). It can provide you with the fundamental methods that all your X++ objects have, such as the default implementations of equal(), toString() and more. It has the infrastructure for your X++ runtime. Because of how XppObjectBase inherits from internal ProxyBase (for kernel interop), it means X++ objects can be passed between AX kernel and your managed code. You generally do not directly use XppObjectBase in your code (you usually cannot instantiate it because it’s something called an abstract class). It can be quite important in scenarios like interop or when identifying your type hierarchy. Some .NET code interacting with X++ objects may for instance see them as XppObjectBase if the exact concrete type cannot be known. Also keep in mind that you usually cannot cast a .NET System.Object directly to an XppObjectBase in X++ – so you would typically have to wrap it actually or instead even populate a known X++ collection to get it to work properly.
XppPrePostArgs: This is actually a supporting class that carries info for your X++ event handlers (pre and post-events). In D365FO with X++ XppPrePostArgs instead of overlayering your code, you can often write some event handler methods that can run either before (pre) or after (post) your base method. The XppPrePostArgs class in X++ is found in the Xpp namespace and usually provides information about the arguments that were passed to your original method and even its return value. In a post-event handler you might call XppPrePostArgs.getArg("paramet1")
to get your original call’s first parameter, or XppPrePostArgs.returnValue()
to get the result that your base method calculated. This is so that you can use or modify it. The class is part of how the system can implement the publisher-subscriber event pattern in X++. You usually write event handlers using X++ attributes (like for example [PreHandler]
or [PostHandler]
) and the framework this way passes an instance of XppPrePostArgs to your handler.
Other components: There are also many system classes in Application object tree (AOT) that can derive from XppObjectBase. For example, most of your kernel classes (like Dictionary classes: DictTable
, DictField
for metadata, or Global
for global functions) tend to be part of your application’s class hierarchy. The Global class provides a lot of static helper methods (like Global::info()
, like Global::error()
, conversion functions, and more).
All these tend to be available without needing to specifically qualify the class name (you can just for example call info()
in X++, which actually is Global::info)
.
RunBase
is considered a base class for older batch framework classes, FormRun
and DataSource
classes for forms, etc. Those tends to be part of the application object framework rather than core Xpp namespace but they still do interact quite closely with it. Your X++ environment has so many system classes (Xpp framework) that provide you with the essential framework between your X++ code and with your underlying runtime (both the AX kernel and .NET Common Language Runtime). When you understand these components it can be come quite important especially when debugging low-level issues or even when you are extending your system’s behavior. One example is writing custom workflow providers or even when you’re deadling with integration points). In most everyday X++ coding you actually tend to interact with them more indirectly.
While the Dynamics 365 FO SDK documentation and Microsoft’s free developer guide on “Application Foundation Classes” can potentially give you a bit more insight into classes like Global, Info, and the dictionary classes, the problem is it can be quite dense, overwhelming and time consuming for you to go into it in detail and you can inquire to Dynamics Edge for more info instead on possible better options for you. You may want to explore options like diving deep into the relationships of the system classes and how the X++ runtime operates (like the mechanism of the AOS, the whole model store, and how X++ code can be compiled and executed in your Microsoft Dynamics 365 Finance and Operations cloud environment).
Working with Excel in X++
X++ can interact with your Microsoft Excel by using the SysExcel classes, which can wrap the Excel COM Interop interfaces. That way you can use X++ read Excel file with these classes meaning you can programmatically create Excel files, you can write to them, read from them, and even control Excel if you need to. The main classes used are: SysExcelApplication
, SysExcelWorkbooks
/ SysExcelWorkbook
, SysExcelWorksheets
/ SysExcelWorksheet
, SysExcelCells
/ SysExcelCell
, and related types (like SysExcelRange
). The workflow can go like:
Create/Open Excel: Use SysExcelApplication::construct()
to create a new Excel application instance sort of like launching Excel in a way (whether in memory on the server or client). From your SysExcelApplication
object, you can get the Workbooks collection by application.workbooks()
.
Open or Add Workbook: With a SysExcelWorkbooks object, you can either call add() to create a new workbook, or call open(filename) to go ahead and open your existing Excel file. This can give you a SysExcelWorkbook object basically representing that file.
Access Worksheet: From the workbook, get the worksheets collection (workbook.worksheets() returning SysExcelWorksheets). You can then go ahead and select a worksheet by index or name – e.g., worksheets.itemFromNum(1) for the first sheet, to return a SysExcelWorksheet
Operate on Cells: From the worksheet, get the cells grid by worksheet.cells() (SysExcelCells). You can go right ahead and access individual cells by their row and column like this: cells.item(row, col) gives a SysExcelCell object. Then you can go ahead and call .value(…) to get or even set the value of your cell. There are also some methods for formatting, like cells.range("A:A").numberFormat("@")
to go ahead and set a whole column to text format. Writing to a cell with a string or number is pretty much as simple as cell.value("Some text");
or cell.value(123.45);
. Reading is done with var myVal = cell.value(); (which then returns an X++ variant type that can be either int, real, str depending.
Save/Close: After making changes, you can save your workbook with workbook.saveAs("filename")
or just even save if it has a name). You can call application.quit()
to close your Excel (especially important on your server to release the active COM instance). If you did open Excel visible (you can control visibility with application.visible(true/false)), then quitting should also close your Excel UI
.
Example – Writing to Excel:
SysExcelApplication excel = SysExcelApplication::construct();
excel.visible(false); // run in background
SysExcelWorkbooks books = excel.workbooks();
SysExcelWorkbook wb = books.add(); // create a new workbook
SysExcelWorksheet sheet = wb.worksheets().itemFromNum(1);
SysExcelCells cells = sheet.cells();
cells.item(1,1).value("Item"); // set header in A1
cells.item(1,2).value("Quantity"); // set header in B1
// Write data in subsequent rows
int row = 1;
while select itemId, onHand from inventTable where inventTable.DataAreaId == curExt()
{
row++;
cells.item(row,1).value(inventTable.ItemId);
cells.item(row,2).value(inventTable.OnHand);
}
wb.saveAs(@"C:\Temp\Inventory.xlsx");
excel.quit();
This kind of code can create you an Excel file listing your item on hand quantities. It is simple example of using the SysExcelApplication and also the related objects to manipulate your Excel from X++Just note that it’s intended almost more as pseudocode or reference example only because in a cloud environment the direct file paths might not be accessible. Instead, often you would want to actually stream to a memory stream or even use SharePoint/Blob storage for the actual file. Also make notes that running your Excel directly on the server might require Office to be installed and appropriately licensed so it may be generally only more suitable for interactive client scenarios or just development/testing. In production, while many choose to use alternate methods like Open XML SDK or library like EPPlus to generate spreadsheets without needing actual Excel application on the server, you may want to consider alternatives or deeper dives on even this and you should consider to inquire to Dynamics Edge for more information about this.
Reading from Excel:
SysExcelApplication excel = SysExcelApplication::construct();
SysExcelWorkbooks books = excel.workbooks();
SysExcelWorkbook wb = books.open(@"C:\Temp\Data.xlsx");
SysExcelWorksheet sheet = wb.worksheets().itemFromName("Sheet1");
SysExcelCells cells = sheet.cells();
str firstCellValue = cells.item(1,1).value(); // read the value in A1
excel.quit();
Reading can be pretty much straightforward. If the cell contains a number, the value might come in as either an int or real in X++, or even as a COM variant. Often you may want to check the COM variant type by cell.valueType()
(which returns a COMVariantType
enum) to know how you can handle it.
Automation vs. Integration: The SysExcel classes tend to use OLE automation (COM) to drive your Excel. In D365FO (cloud) note that server-side Office automation may not be directly supported in production and contact Dynamics Edge for more info or details on this if you may be stuck or not sure what to do next.
These classes are usually used in your client-side scenarios (or back in AX 2012 on your client). In your D365FO, perhaps a more common approach for exporting to Excel is by using Data Entities or even the Open in Excel feature. However, for completeness and for your batch scenarios in on-prem or your dev environments, SysExcel classes are still part of X++ anyway. There is also an ExcelIO class in AX 2012 for some simpler CSV imports and exports. However, note that all these are ways that probably don’t really work that well in modern enterprise D365FO and contact Dynamics Edge for some more serious options you can consider in that case.
X++ gives you the tools you may need to interact with Excel, but please be mindful of the environment in which your code runs and contact Dynamics Edge for options on how you can do this in cloud D365FO instances if you have trouble with traditional methods that may only work on-prem. That means even SysExcelApplication usage (e.g., reading cell ranges, formatting cells, using formulas) could come up short for modern D365FO environments.
Workflow in X++ (Dynamics 365 Finance and Operations)
In Dynamics 365 Finance and Operations, Workflows are actually some mechanism to automate and enforce your business processes (such as your approvals for documents like your purchase requisitions and expense reports as examples). From a high-level perspective, your Dynamics 365 Finance and Operations workflow is kind of like a sequence of steps (tasks, approvals, decisions) that your document or record goes through. It often involves some user approvals. Workflows in D365FO tend to be configured through the D365FO workflow UI (by using a visual Workflow Editor), but as a developer, you may also want to use Dynamics 365 X++ workflows to define your building blocks of the workflows and even to extend workflow functionality beyond what’s possible only by configuration or customization of Dynamics 365 Finance and Operations out of the box.
Workflow Concepts: A workflow tends to be defined by what’s called a Workflow Type (also sometimes known as a template). That specifies the entity (table) it can apply to and even which kinds of elements can be inside of it. Workflow elements tend to include tasks (like manual steps for a user to perform), include approval steps (where a user or group may have to approve), and things like automatic tasks (which for example can be code executed automatically).
As a developer, creating a new workflow generally involves quite a few things, like:
Designing a Workflow Type in AOT (under Business Process and Workflow > Workflow Types). This is generally done by you creating a class that extends WorkflowDocument and having metadata that links to the target table. You also tend to specify your menu items for submission, and any custom conditions (like “can submit” logic through overridden canSubmitToWorkflow method on the table). You specify what workflow elements (tasks/approvals) can be available.
Defining any custom workflow tasks or approvals (if needed) in AOT under Workflow Tasks/Approvals. Often, out-of-the-box tasks (like an approval on a particular document) can be reused via configuration, but the problem is it can be a limitation for some scenarios. So you might create custom automated tasks that run X++ code to help with that. Once type and components are in place, your administrator can use the Dynamics workflow editor to go ahead and assemble the actual workflow (adding steps and approvals graphically in a familiar interface, but perhaps adding blocks that wouldn’t be otherwise possible out of the box). The workflow can then be activated for the low-code or no-code proper use of your D365FO workflow system admins or citizen developers / admins.
Workflow Runtime: When your user submits your document to workflow (usually by clicking a “Submit” button in the workflow framework), your system creates a workflow instance. The workflow execution goes ahead and gets handled by the Dynamics 365 X++ Workflow runtime which actually runs on the server in a batch job (that is called Workflow message processing). The workflow engine goes ahead and processes each step from beginning to end in sequence. If the workflow encounters an Approval step, it will go ahead and assign a work item (it may become a task in a user’s workflow inbox) to the designated user or role and then go ahead and wait for them to approve or reject this. Now if it’s a manual task, it may create a similar kind of work item for someone to go ahead and complete the task. The workflow can then branch out (through conditional decisions, through parallel branches) and ultimately can end when it finally reaches a completed state. Throughout this whole process your events are logged – and you can even trigger Power Automate by Business Events for workflow for advanced scenarios or modern cloud integrations. From X++ perspective, you might go ahead and interact with your workflow by calling Workflow::activateFromXpp(…) or similar APIs to programmatically start or to progress a workflow. Using event handlers to react to your workflow events (for example, you could have X++ code trigger when your workflow is approved in order to perform some extra actions. Implementing custom logic in canSubmitToWorkflow() (on the table) to control when a record is allowed to be submitted, or even submitToWorkflow() to provide your workflow with required data on submission. Creating custom automated tasks by writing some classes that implement WorkflowWorkItemHandler so this way you can use the X++ D365FO WorkItemHandler delegate to help you run code as some step in your workflow (not requiring user action). For more information on X++ delegate they can be very powerful and you can inquire for custom training to include coverage on X++ delegates.
Simple Example: Suppose we have a workflow for a Purchase Order approval. The workflow type can be defined for the Purchase Order table. When PO is submitted, X++ (on your form or the table’s submitToWorkflow) calls workflow framework to create a new instance of the “Purchase Order Approval Workflow”. Now this workflow might first have an automated task (some X++ code that checks something first like if the budget exists). Then, an approval step might be assigned to the purchasing manager.
The manager might get a notification (work item) to go ahead and approve the PO. When they do approve, your workflow continues. Maybe it has a second approval or even just ends, with marking the PO as approved (the workflow framework will update status fields on the record, e.g., WorkflowStatus = Approved).
All of these transitions and assignments can be managed by the standard workflow runtime. As an X++ developer your involvement can be how to understand defining workflow type and also for any custom tasks/handlers, possibly handling the result (like code to execute when the workflow completes successfully or is cancelled).
Workflow-related Classes: In X++, many workflow classes are in the Workflow namespace/module. For example, Workflow::activate() methods, WorkflowWorkItemActionManager (to complete or delegate your tasks), SysWorkflowQueue (your batch handler). You typically do not really need to modify these, but to just use them by the framework. There are also tables like your WorkflowInstanceTable, WorkflowTrackingTable that log your D365FO workflow progress. If your workflow fails, entries in your tracking table and Infolog can help you really debug it.
Events and Business Events: D365FO also emits some workflow business events (for integration with Power Automate or your other systems) – like an event when a workflow is started or is completed. This allows for some no-code or low-code reactions to your D365 FO workflow progress, outside of X++ such as for your citizen developers to use where possible.
You can ask Dynamics Edge about for more info beyond this. The Dynamics 365 FO Workflow documentation is quite extensive but can be tiresome to go through and some scenarios may not even be covered. A good starting point can be to ask Dynamics Edge about D365FO canSubmitToWorkflow, how to use the Workflow editor, how to debug workflows (since they run in batch), how to extend the workflow system (custom approvals or applying workflow to new document types). and more. Understanding the workflow infrastructure in D365FO can really empower you to implement your complex approval processes tailored to your business and organizational needs and scenarios with the power of X++.
For some ideas of more advanced topics, here’s a possible start:
SysObsoleteAttribute
In X++, the SysObsoleteAttribute
class is used to mark your elements like classes, methods, or variables as obsolete. It shows that they are outdated and should preferably not be used in new development. When an element is decorated with this attribute, you tell the compiler to generate a warning or error when your code ends up being compiled. That depends on the attribute’s configuration though. It’s for maintaining code quality and guiding your developers to some preferred implementations. If a method yourOldMethod
is marked as obsolete with an explanation and the isError
flag set to true, any attempt to use yourOldMethod can result in a compilation error. That can encourage your developer to use the recommended alternative. Understanding and leveraging SysObsoleteAttribute means you can have a good deprecation process and keep legacy code transitions going smoothly to your newer standards.
SalesFormLetter Run Exception Handling in D365FO X++
In Dynamics 365 Finance and Operations (D365FO), where SalesFormLetter Run Exception Handling in D365FO X++ comes in is when the SalesFormLetter class is so important for key processes like confirming your sales orders, posting packing slips, even for generating your invoices. However, during these, exceptions may occur from time to time because of issues like missing mandatory data or even configuration errors. Implementing some robust exception handling using your try…catch blocks especially around the run method of SalesFormLetter can be so important here to managing your exceptions in this scenario and similar ones more effectively. Making sure that errors are caught and handled gracefully and the next operations to continue well with less interruptions. You can consider wrapping the run method in a try block and catching specific exceptions which means your system can log errors. It can notify users or even implement retry mechanisms as you may need it to work. Proper exception handling here really can make a difference in the resilience and reliability of all your sales order processing workflows in D365FO and across your Dynamics 365 and Power Platform environments.
microsoft.dynamics.ax.application.devalm.buildxpp
When you talk about microsoft.dynamics.ax.application.devalm.buildxpp
component you are talking about the build automation process in Dynamics 365 Finance and Operations (D365FO) that helps get done your compilation and packaging of your X++ code. It’s for your customizations and developments to be built and deployed well. This component often gets leveraged along with continuous integration and continuous deployment (CI/CD) pipelines to streamline your whole development lifecycle end to end. On the way automating your repetitive tasks and also reducing the potential for human error on your end. Understanding configuration and integration within your build environments is important for developers like you who want to maintain efficient reliable deployment processes and solutions.
microsoft.dynamics.ax.framework.workflow.workfloweditorhost.application
When you talk about microsoft.dynamics.ax.framework.workflow.workfloweditorhost.application
that means your workflow editor host in Dynamics 365 Finance and Operations framework as key component powering your interface and tools which are needed for the designing, editing, and managing of your workflows. It means users can create those complex business process flows that automate your tasks and approvals effectively. It helps a lot with your operational efficiency. Developers and system administrators can get more responsible with implementing and maintaining workflows which align with organizational processes by becoming more familiar with this.
Cannot Edit a Record in Purchase Order Update (PurchParmUpdate): The Record Has Never Been Selected
If you get stuck with error message “Cannot edit a record in Purchase Order Update (PurchParmUpdate): The record has never been selected” it means you try to modify a record in the PurchParmUpdate table without having properly selecting it for the update. In X++, in order to be able to edit a record, it must first be selected properly with an exclusive lock by using the select forupdate statement. This means you get data integrity by preventing your concurrent processes from modifying the exact same record at the same time. To resolve this kind of error, review your code carefully to check that any update operations on PurchParmUpdate records are start with a correct selection statement that also includes the forupdate keyword in the correct way. If you still have trouble preventing runtime errors during purchase order updates i0n your case, please contact Dynamics Edge and we can see how we may help you with our custom X++ solutions, custom X++ consulting and custom X++ training options for advanced, verticalized or specialized scenarios such as yours.
The View Query Has Been Overridden Because of a Conflict
The warning message “The view query has been overridden because of a conflict” shows up when there can be conflicting definitions or even modifications associated with a view’s query in Dynamics 365 Finance and Operations. This conflict can happen because of multiple customizations or extensions that are trying to change the same query. That can lead your system to override one definition with another one. To address this issue fully, review all your customizations and extensions thoroughly which may be affecting the view in question. Check that modifications are compatible and do not accidentally interfere with each other. Leverage good extension methodologies like event handlers or chain of command patterns that can help prevent these kinds of conflicts and keep integrity of view queries, but if you still have trouble, ask Dynamics Edge and see if we can help you further in your case.
Cannot Edit a Record in Inventory Transactions (InventTrans): The Record Has Never Been Selected
The error “Cannot edit a record in Inventory Transactions (InventTrans): The record has never been selected” might be similar to the “Cannot edit a record in Purchase Order Update (PurchParmUpdate): The record has never been selected” except in this case instead of PurchParmUpdate it might be an InventTrans record that attempted to be modified without it being properly selected for the update first. In X++, when you start modifying a record you need to be selecting it first with an exclusive lock by properly using the select forupdate statement in the right way. The record has to be locked for editing, to prevent any stray modifications from happening at the same time that could lead to inconsistencies in your data or corruptions. Carefully check your code that seems to not be working properly or seems to be throwing the error, for the operation trying to update InventTrans records including a proper selection statement beforehand with the forupdate keyword in it. Not sure? Ask Dynamics Edge and we can check if we may help you with this or other similar advanced issues for your Dynamics 365 Finance and Operations X++ development success!
Have a Question ?
Fill out this short form, one of our Experts will contact you soon.
Call Us Today For Your Free Consultation
Call Now