Audience: CE developers moving into F&O or working across both.
Goal: map concepts, components, and patterns one-to-one with concise examples.
Big Picture
| Aspect | CE (Customer Engagement / Dataverse) | F&O (Finance & Operations) |
| Purpose | CRM workloads (sales, service, marketing) | ERP (finance, supply chain, production) |
| Runtime | Multi-tenant SaaS | AOS server per environment |
| Code | C#, JavaScript, Power Automate | X++ (compiled to IL/CIL), SysOperation |
| Dev machine | Browser only | DevBox VM (Tier-1) |
Data Layer
| Concept | CE | F&O |
| Table | Dataverse table | AOT table (compiled) |
| Field/Column | Dataverse column | Table field (often via EDT) |
| Add field to Microsoft OOB table | Direct in solution | Table extension (fields live in the extension but become real columns) |
| Custom table | Create in solution | Create table and add fields directly (no extension) |
| Reusable type | — | EDT (Extended Data Type) |
| Relations | N:1, 1:N, N:N | Table relations, RefRecId, enums |
| Keys | Primary, alternate | Primary keys, indexes (clustered/nonclustered) |
UI Layer
| Concept | CE | F&O |
| Form/page | Model-driven form | Form with patterns (Detail, ListPage, Workspace) |
| View/list | Views | Grids, queries |
| Custom control | PCF | Extensible Control (TS/JS, contract-bound) |
| Client scripting | JavaScript via Xrm.* | No Xrm; optional Extensible Control; business logic in X++ |
| Simple rules | Business rules | Form methods + validate/modified |
Logic & Pipeline
| Topic | CE | F&O |
| Server hooks | Plugins (C#) on staged pipeline | CoC on specific methods:
validateField,
modifiedField,
validateWrite, insert/update/delete |
| Flow/automation | Power Automate | Batch/SysOperation, Business events |
| Transaction | Managed by Dataverse | ttsBegin/ttsCommit; checkFailed() or returning false cancels save |
| Execution order | PreValidation → PreOperation → PostOperation | One chain per method; next <method>() calls base/previous extension (sequential) |
Plugin Stages (CE) vs CoC Methods (F&O)
| CE plugin stage | CE plugin When it runs | F&O CoC hook | F&O Purpose
|
| PreValidation | Before DB txn, before main pipeline | validateField(), modifiedField() | Field-level checks and reactions |
| PreOperation | Inside txn, before write | validateWrite() | Row-level gate; return false to cancel |
| Operation | — | insert() / update() (ttsBegin/ttsCommit) | Actual persistence logic |
| PostOperation | After write, still in txn | Post-handlers or post-update() | Side-effects that need the same txn |
| Async (Post) | After commit, async | Batch/SysOperation, Business events | Out-of-band processing |
F&O Save Sequence (Simplified)
| Step | Meaning |
validateField() | Is the new field value allowed? |
modifiedField() | React to change (defaults, lookups, recalcs) |
validateWrite() | Gate before save (create & update) |
ttsBegin → insert()/update() → ttsCommit | Transactional persistence |
| Delete path | validateDelete() → delete() |
Labels & Localization
| Aspect | CE | F&O |
| Where | Solution labels | Label files (.ald) per model/language |
| Usage | Captions, choices | All text: captions, errors, tooltips; referenced as @Model:LabelId |
| Storage | Platform-managed | Compiled resources in the package |
Integration
| Need | CE | F&O |
| API | Dataverse Web API | OData via Data Entities; custom services |
| Files/ETL | Dataflows / ADF | DMF (CSV/Package), Electronic Reporting, Recurring Integrations |
| Events | Webhooks, plugins | Business events, Batch |
Reporting & Analytics
| CE | F&O |
| Power BI, views, Advanced Find | SSRS (DP/Contract/Report), Power BI, Inquiry pages, ER |
Security
| CE | F&O |
| Roles, privileges, teams | Role → Duty → Privilege → Entry point (+ table permissions) |
ALM & Environments
| CE | F&O |
| Dev/Test/Prod orgs; solution moves | DevBox for code, Build server, Tier-2+ Sandboxes/Prod via LCS; deployable packages |
Extensible Control vs PCF
| Topic | PCF (CE) | Extensible Control (F&O) |
| Host | Dataverse form | F&O form control |
| Data access | context.parameters | Bound properties + control contract |
| Deploy | Solution import | In model/package; deploy via build/LCS |
| Role | Can carry logic | UI widget only; business rules in X++ |
Typical Patterns (with Examples)
| Task | CE way | F&O way |
| Add field to OOB table |
New column in solution |
Table extension + field (via EDT); Form extension to show it |
| Required-field validation |
Business rule or PreOp plugin |
CoC on validateWrite(); checkFailed("@My:Label") + return false |
| Lookup |
Lookup column |
EDT with Relation + ReferenceGroup control (auto-lookup) |
| Background job |
Flow or plugin (async) |
SysOperation (contract/service/controller) + batch |
| Export vendors |
Web API / Dataflow |
DMF Export VendVendorV2Entity (CSV) |
| Custom UI widget |
PCF |
Extensible Control (TS/JS) |
Mini X++ Example: CoC Required Field
// HX_VendTable_ValidateWrite_CoC
// ----------------
// Enforces non-empty HX_ExternalRefId on vendor save.
//
// Sep 20, 2025: Created by Forrest Zhang for Bootcamp D01
[ExtensionOf(tableStr(VendTable))]
final class HX_VendTable_ValidateWrite_CoC
{
public boolean validateWrite()
{
boolean ok = next validateWrite();
if (!ok) return ok;
if (this.(fieldNum(VendTable, HX_ExternalRefId)) == '' || this.(fieldNum(VendTable, HX_ExternalRefId)) == null)
{
checkFailed("@HX:ExternalRefIdRequired");
return false;
}
return true;
}
}
Quick Answers
- Do fields in a table extension become real columns? Yes—on Microsoft OOB tables. On your own custom table, add fields directly.
- Is there
validateCreate()? No. Use validateWrite() for create/update; use CoC on insert() for create-only logic.
- AOS & CIL? AOS = Application Object Server (runtime). X++ compiles to .NET IL/CIL; AOS executes it.
Author: Forrest Zhang — Dynamics 365 CE & F&O Developer
No comments:
Post a Comment