X++ Basics and Operators
Overview: X++ is an object-oriented programming language used in Microsoft Dynamics 365 Finance & Operations (formerly AX). When you think about the notion about what your x++ language reference is and what it is used for its syntax and features are similar to languages like C# and Java. X++ code is developed in Visual Studio and runs on the Dynamics application platform.

The language supports classes, inheritance, and integrates seamlessly with the data dictionary and SQL database of the ERP system.
Syntax & Operators: X++ supports a rich set of operators and language constructs comparable to other C-style languages:
- Assignment and Increment: Use the standard
=
for assignment and compound assignments like+=
or-=
. Increment (++
) and decrement (--
) operators are available to adjust numeric values by 1. - Arithmetic and Bitwise: Arithmetic operators include
+
,-
,*
,/
for addition, subtraction, multiplication, and division. X++ also provides special keywordsDIV
andMOD
for integer division and remainder operations (e.g.x DIV y
gives the integer quotient,x MOD y
gives the remainder). Bitwise operators such as<<
(left shift),>>
(right shift),&
(AND),|
(OR),^
(XOR), and~
(NOT) are supported for integer types. - Relational (Comparison): You can compare values using
==
(equals),!=
(not equal),<
,>
,<=
,>=
. These operators return boolean results as expected. Notably, string comparisons with==
in X++ are case-insensitive by default (unlike C#). - Logical (Boolean): Logical conjunctions use
&&
(AND) and||
(OR), and the unary!
operator denotes logical NOT. These work with boolean expressions in conditional statements (if
,while
, etc.). - Conditional/Ternary: X++ supports the ternary conditional operator
?:
just like C#. For example,result = (condition) ? expr1 : expr2;
will evaluate toexpr1
if the condition is true, otherwiseexpr2
. - Control Structures: The language provides
if-else
conditionals,switch
statements (which in X++ do not require abreak
in each case, though it’s recommended), and loop constructs likewhile
,do-while
,for
, andforeach
(for container or collection iteration). Exception handling usestry-catch
similar to C#.
Data access: One distinctive feature of X++ is its integrated SQL-like data access syntax. For example, select
statements are written directly in X++ to query tables, and keywords like firstOnly
, forUpdate
, etc., refine query behavior. The language also provides set-based operations like insert_recordset
and classes like RecordInsertList
for efficient database interaction (covered below).
DataContractAttribute
The [DataContractAttribute]
is used to declare a class as a data contract in X++. Applying this attribute to an X++ class designates that class for data serialization, typically for the purpose of exchanging data in service calls or batch processing. In practice, a data contract class holds parameters that will be passed to a service or operation. Marking a class with DataContractAttribute
indicates that the class can be serialized and used as a container for structured data. This attribute is placed above the class declaration. For example:
[DataContractAttribute]
public class MyOperationContract
{
// Define data fields and parm methods here
}
In Dynamics 365 finance and operations SysOperation framework (the batch execution framework in AX/D365), any class that provides input parameters to a service must be a data contract class decorated with this attribute. Without it, the framework will not recognize the class for automatic UI dialog generation or data packing. Essentially, DataContractAttribute
ensures the class is prepared for serialization (converted to XML, for instance) when the operation is executed in a different session or tier.
DataMemberAttribute
Within a data contract class, individual data fields are exposed via methods decorated with [DataMemberAttribute]
. This attribute is applied to public getter/setter methods (often called parm methods) and indicates that the method’s value should be serialized as part of the contract. In other words, DataMemberAttribute
marks a method as a data field in the contract’s schema. The method should follow the pattern of a typical parm method: accepting an optional parameter to set the value and returning the current value. For example:
[DataContractAttribute]
public class MyOperationContract
{
int _someValue;
[DataMemberAttribute("SomeValue")]
public int parmSomeValue(int _val = _someValue)
{
_someValue = _val;
return _someValue;
}
}
In the above snippet, the parmSomeValue
method is decorated as a data member with a specified name “SomeValue”. At runtime, the SysOperation framework will include a field for this parameter on the dialog, and it will serialize this value when the operation executes. The DataMemberAttribute
can only be used on instance methods (not on variables directly) that adhere to the get/set pattern (one optional parameter and a return of the same type). This attribute essentially flags which properties of the data contract class should be exposed to the UI and persisted for the operation. (Any public parm method without DataMemberAttribute
is ignored by the framework.)
SysOperationServiceController
SysOperationServiceController
is the base controller class in the dynamics 365 finops SysOperation framework (Batch job framework). A controller class orchestrates the execution of a SysOperation. In practice, you create a new controller class that extends SysOperationServiceController
to launch your operation. The controller’s responsibilities include handling the dialog for user input, specifying which service class and method to invoke, and managing the execution mode (synchronous vs. batch).
Typically, the controller class overrides the new()
constructor to set up the link to the service and data contract, and provides a main()
method to initiate the process. For example:
public class MyOperationController extends SysOperationServiceController
{
public void new()
{
super(classStr(MyOperationService), methodStr(MyOperationService, processOperation));
this.parmDialogCaption("My Operation Parameters");
}
public static void main(Args args)
{
new MyOperationController().startOperation();
}
}
In this sample, the controller’s constructor calls super
with the service class name and method name that it will execute. The parmDialogCaption
sets a custom title for the auto-generated dialog. Calling startOperation()
in main()
will open the dialog (for interactive runs) and then execute the operation according to the specified execution mode. The controller can control whether to show the dialog, use a progress bar, run in the background, etc. Notably, it holds the execution parameters for the operation, such as the SysOperationExecutionMode
(see below) and the reference to the data contract instance. In summary, SysOperationServiceController
classes direct the batch operation by gathering input and starting the process.
SysOperationServiceBase
SysOperationServiceBase
is the base class that service classes extend in the dynamics 365 SysOperation framework. A service class contains the actual business logic that will be executed, typically in a method marked as a SysEntryPoint. Developers create a new service class (often named with “Service” suffix) that extends SysOperationServiceBase
and implements one or more operations. These operations are just methods (usually public
) that perform the desired task. The service class should be run on the server tier (in D365 FO this is set via the class’s RunOn property or by convention) and should not directly handle any UI. All user interaction is meant to happen through the controller’s dialog.
For example, a minimal service class might look like:
public class MyOperationService extends SysOperationServiceBase
{
[SysEntryPointAttribute]
public void processOperation(MyOperationContract contract)
{
// Business logic using data from the contract
int val = contract.parmSomeValue();
// ... (perform operations, e.g., update records, etc.)
info(strFmt("Processed value %1", val));
}
}
Here, MyOperationService
is the service class, and processOperation
is the method that does the work. We pass in the MyOperationContract
data contract so the method can use the user-provided parameters. The method is decorated with [SysEntryPointAttribute]
to indicate it’s an entry point for the framework (this is required for security and invocation through the d365fo SysOperation pipeline). The service class should focus on business logic only, keeping with the single-responsibility principle (avoid putting UI or control logic here). The SysOperation framework will call the specified service method (via the controller) either immediately or via batch, and handle serialization of the contract data automatically.
SysOperationExecutionMode
The SysOperationExecutionMode
is an enumeration that determines how d365 SysOperation is executed where common values tend to include Synchronous
, Asynchronous
, and ScheduledBatch
. By default, controllers run in Synchronous mode, meaning the operation executes immediately in the current session (the user may see a progress dialog and waits until completion). If the operation is long-running or not time-sensitive, a developer can choose Asynchronous or ScheduledBatch execution for better user experience or load management.
- Synchronous: The operation runs on the same thread and the user waits until it finishes. Use this when the result is needed right away or for quick tasks.
- Asynchronous: The operation is started in the background on the AOS (Application Object Server) but without scheduling a formal batch job. The user can continue working in the UI immediately after initiating the operation. This mode is useful for long tasks where you don’t need a scheduled batch, but you also don’t want to block the UI.
- ScheduledBatch: The operation is run via the batch framework – it gets queued as a batch job and executed on a batch server. This is ideal for heavy processing that should be offloaded completely. When using this mode, the SysOperation framework will create a batch task record. (The user typically has an option on the dialog to check “Batch execution”, which the controller can default or enforce.)
You can set the execution mode in code by calling controller.parmExecutionMode()
. For example:
MyOperationController controller = new MyOperationController();
controller.parmExecutionMode(SysOperationExecutionMode::ScheduledBatch);
controller.startOperation();
In the above, we explicitly force the operation to run as a scheduled batch job. If a controller is constructed without specifying an execution mode, it defaults to Synchronous. Use asynchronous modes for operations where immediate feedback isn’t necessary. Synchronous mode is appropriate when users expect the outcome instantly (for instance, updating the current form). By thoughtfully choosing the SysOperationExecutionMode
, you can improve user experience and system performance (e.g. batch jobs can run on separate threads or servers). Keep in mind that ReliableAsynchronous is a newer mode (in D365 FO) related to “real async” features, which ensures robust async execution similar to batch, but without explicit scheduling – it can be used for certain operations to avoid blocking the client. (Most typical scenarios use the three modes described above.)
insert_recordset
In X++, insert_recordset
is a set-based SQL operation used to insert multiple records into a table in a single round trip to the database. It allows you to copy data from one or more source tables directly into a destination table, optionally with filtering or aggregation, all in one go. This is much more efficient than inserting records one-by-one in a loop because it reduces the number of database calls.
Usage: The syntax for insert_recordset
is:
insert_recordset DestinationTable (FieldList)
select FieldList from SourceTable [where …] [group by … joins etc.]
You specify the target table and fields to insert, then provide a select query that retrieves the data to insert. The fields in the target must correspond to the fields (or expressions) in the select. If the field lists match exactly in order and type, you can even omit them for simplicity.
For example, consider we have a table NameValuePair
with fields Name and Value, and we want to create summary records in another table ValueSumByName
that stores each Name with the sum of its values. We can do:
ValueSumByName valueSum;
NameValuePair source;
insert_recordset valueSum (Name, ValueSum)
select Name, sum(Value)
from source
group by Name;
This single statement will read all records from NameValuePair
, aggregate the Value by Name, and insert the results into ValueSumByName
. The entire operation happens on the database server side in one trip, making it extremely fast for bulk data movement. Another use is inserting data from variables or expressions – you can include literal values or computed values in the select (except that you cannot directly select pure literals; a trick is to select from a single-row source or use firstonly
to ensure one insert). If needed, you can also join multiple source tables in the select.
Using insert_recordset
has some caveats: if the operation cannot be executed as a set-based query (for example, if it involves complex X++ business logic per record), the system might fall back to row-by-row insertion. But in general, whenever you can express the data insertion with a query, insert_recordset
is the preferred approach for performance. It automatically handles mapping of fields and assignment of system fields like RecId. After an insert_recordset
, you can check the rowCount()
on the table buffer to see how many rows were inserted.
SysObsoleteAttribute
The [SysObsoleteAttribute]
is an attribute used to mark classes or methods as deprecated in X++. It serves as a warning or error mechanism for developers. When you decorate an X++ element with this attribute, the compiler will either emit a warning or an error if that code is referenced elsewhere, depending on how the attribute is configured. The attribute takes two parameters: a message string (usually explaining the deprecation or recommended alternative) and a boolean flag. If the flag is set to true
, using the marked element causes a compile error; if false
, it causes a compile warning.
Example usage: Suppose we have a method that is outdated and we have a new method to use instead. We can mark the old method as obsolete:
public class SomeApi
{
[SysObsoleteAttribute("Use DoSomethingNew instead", false)]
public void DoSomething()
{
// ... old implementation ...
}
public void DoSomethingNew()
{
// ... new implementation ...
}
}
In this example, the DoSomething
method is tagged with SysObsoleteAttribute
. The message "Use DoSomethingNew instead"
will appear in the compiler output as a warning to anyone calling DoSomething
. Because the second parameter is false
, it is a soft deprecation (warning). Over time, after consumers have had a chance to migrate to the new method, this can be elevated to an error by changing the flag to true
(making it a hard deprecation). Using SysObsoleteAttribute
is the recommended practice to phase out old code in public APIs without immediately breaking existing customizations. It allows developers to get warnings about usage of deprecated elements so they can update their code before the old implementation is completely removed in a future release.
SysOperation Framework Overview
The SysOperation framework (also known historically as the Business Operation Framework) is the recommended pattern for implementing operations and batch processes in Dynamics 365 FO/AX. Introduced in AX 2012, it replaced the older RunBase/RunBaseBatch framework for most batch jobs and processes. SysOperation is built on an MVC-like separation of concerns and uses .NET attributes for serialization of parameters. In this framework:
- The Model is represented by the Data Contract class (parameters container, marked with DataContract/DataMember as discussed).
- The View is the automatic dialog generated for those parameters. The framework can generate a standard dialog form for the data contract, and you can customize it with a UI Builder class (extending
SysOperationAutomaticUIBuilder
) if needed for advanced layouts or validations. - The Controller orchestrates the operation by tying the contract and service together (often extends
SysOperationServiceController
). - The Service (Controller in MVC terms) is the Service class (extends
SysOperationServiceBase
) that contains the business logic to execute, typically via a method decorated with[SysEntryPoint]
.
This design allows developers to write operations that can run either interactively or in batch with minimal code changes. When running interactively (synchronously), the user is presented with the parameters dialog and the operation executes immediately. When scheduled as a batch, the same operation is executed on the batch server asynchronously. The framework handles packaging (serializing) the parameters and results automatically, using the Data Contract attributes to know what to pack. It also integrates with the security framework (methods must be entry points to be callable) and with the infrastructure for batch processing (like the batch queue and history).
Key features: The SysOperation framework provides built-in support for parameter serialization (no more writing pack/unpack code manually), validation through attributes, and easier upgrade of processes. It leverages .NET runtime so you can even offload execution to CIL (Common Intermediate Language) for performance, if needed. The framework is highly extensible; for example, you can add a UI builder to modify the dialog behavior or use the SysOperationContractProcessingAttribute
for custom initialization. Microsoft’s own application modules use SysOperation for many tasks (e.g. posting documents, running business processes) since it is more scalable and testable than the old RunBase pattern.
In summary, the SysOperation framework is the standard way to implement operations that might need to run in the background or with user-specified parameters. By splitting the work into a contract, a service, and a controller, it promotes a clean separation of UI from business logic and supports execution flexibility (interactive vs. batch) out of the box. This makes it easier to maintain and extend processes in Dynamics 365 Finance and Operations.
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