Number Sequences in Dynamics 365 Finance & Operations
Question 13 — Generating unique record IDs: setup, the NumberSeq class, and continuous vs. non-continuous.
Interview Question
NumberSeq class.
Model Answer (Short)
A number sequence is the framework that generates unique, formatted identifiers for records —
such as sales order IDs, invoice numbers or custom document numbers. Setup involves an EDT that
represents the ID, a number sequence reference registered through the
NumberSeqModuleXxx class (via the loadModule method), and a number sequence
code configured in the setup wizard or parameter form. In code you consume it with the
NumberSeq class — typically
NumberSeq::newGetNum(...) to fetch the next value. A continuous sequence guarantees no
gaps (numbers can be recovered if a transaction is aborted), whereas a non-continuous sequence is
faster but may leave gaps.
Detailed Explanation
Setup Steps
- Create an EDT for the identifier (e.g. based on a string type) — this is what the sequence populates.
-
Extend the module's
NumberSeqModule...class and register the reference inloadModule()usingparmReferenceHelp,parmWizardIsContinuousandparmWizardIsManual. - Run the number sequence setup wizard (or configure manually) to create the number sequence code and assign a format/segment.
- Reference the number sequence in the relevant parameters form so it can be resolved at runtime.
Continuous vs. Non-continuous
- Continuous — no gaps allowed. If a transaction is cancelled, the number is returned to the pool via a cleanup process. Slower because it locks to preserve order; required where legal/audit numbering demands no gaps.
- Non-continuous — allows gaps; numbers are pre-allocated in blocks for performance. Preferred where gaps are acceptable (better throughput and fewer locks).
Prerequisites (Rule 5)
- Visual Studio with the Dynamics 365 developer tools.
- A custom model / package for your objects.
- An EDT for the identifier field.
- A registered number sequence reference and a configured number sequence code.
Code Example — Registering a reference (loadModule)
// Inside a class extending NumberSeqModule... (e.g. NumberSeqModuleAbc)
protected void loadModule()
{
NumberSeqDatatype datatype = NumberSeqDatatype::construct();
datatype.parmDatatypeId(extendedTypeNum(AbcOrderId));
datatype.parmReferenceHelp("Unique key for the Abc order.");
datatype.parmWizardIsContinuous(false); // non-continuous
datatype.parmWizardIsManual(NoYes::No);
datatype.parmWizardIsChangeDownAllowed(NoYes::No);
datatype.parmWizardIsChangeUpAllowed(NoYes::No);
datatype.parmSortField(1);
datatype.addParameterType(NumberSeqParameterType::DataArea, true, false);
this.create(datatype);
}
Consuming the number sequence in code
public AbcOrderId getNextOrderId()
{
NumberSeq numberSeq;
AbcOrderId orderId;
// Resolve the number sequence configured on the parameters table
numberSeq = NumberSeq::newGetNum(AbcParameters::numRefAbcOrderId());
orderId = numberSeq.num();
return orderId;
}
Number sequence reference on the parameters table
// Static helper on the parameters table returning the reference
public static NumberSequenceReference numRefAbcOrderId()
{
return NumberSeqReference::findReference(
extendedTypeNum(AbcOrderId));
}
Cleaning up an unused continuous number
NumberSeq numberSeq = NumberSeq::newGetNum(AbcParameters::numRefAbcOrderId());
AbcOrderId orderId = numberSeq.num();
// If the record is NOT saved, return the number (continuous sequences)
if (!recordSaved)
{
numberSeq.abort(); // releases the number back to the sequence
}
Continuous vs. Non-continuous
| Aspect | Continuous | Non-continuous |
|---|---|---|
| Gaps | Not allowed | Allowed |
| Performance | Slower (locks to preserve order) | Faster (pre-allocated blocks) |
| Recovery on abort | Number returned via abort() / cleanup |
Number simply lost (gap) |
| Typical use | Legal / audit numbering (invoices) | Internal IDs where gaps are fine |
Points the interviewer wants to hear
- Number sequences generate unique, formatted IDs for records.
- Setup = EDT + reference in
loadModule()+ configured code + parameters form. - Consume with
NumberSeq::newGetNum(...)then.num(). - Continuous = no gaps but slower; non-continuous = faster with gaps.
- Use
abort()to return an unused number in a continuous sequence.
Likely Follow-up Questions
- When would you choose a continuous sequence despite the performance cost?
- What does
numberSeq.abort()do and when is it needed? - What is the role of the scope / segment (e.g. DataArea) in a number sequence?
- How do you make a number sequence manual so users can type their own value?
Key Takeaway
Number sequences provide unique, formatted identifiers tied to an EDT and configured through a
reference. Choose continuous when gaps are unacceptable (audit/legal) and
non-continuous for performance, and always consume them through the NumberSeq class.