L’endpoint Web API e il modello OData v4
Dataverse espone i propri dati e la propria logica tramite una Web API RESTful conforme a OData v4, raggiungibile all’indirizzo https://<org>.crm.dynamics.com/api/data/v9.2/. È il canale di integrazione preferito per client esterni perché è puramente HTTP + JSON, non richiede assembly proprietari e funziona da qualsiasi linguaggio. L’autenticazione avviene tramite Microsoft Entra ID con OAuth 2.0: si registra un’app in Entra, si concede il permesso delegato o applicativo su Dataverse e si acquisisce un bearer token.
I set di entità usano il nome plurale logico (accounts, contacts, new_projects) e ogni riga è identificata dalla primary key GUID nell’URL: /accounts(<guid>).
Operazioni CRUD
- Create:
POST /accountscon il corpo JSON. La risposta di default è204 No Contentcon l’headerOData-EntityIdche contiene l’URI del record creato. - Retrieve:
GET, con le query option OData$select,$filter,$orderby,$top,$expandper navigare le relazioni. Usa sempre$selectper limitare le colonne e ridurre payload e consumo di risorse. - Update:
PATCH /accounts(<guid>)— aggiornamento parziale, solo i campi inviati. Un PATCH su un record inesistente può eseguire un upsert. - Delete:
DELETE /accounts(<guid>).
Per associare record correlati si usa l’annotazione @odata.bind: "primarycontactid@odata.bind": "/contacts(<guid>)".
L’header Prefer per il controllo della risposta
Prefer: return=representationfa restituire il record completo aggiornato subito dopo un POST/PATCH (risposta201/200con il body), evitando una GET successiva.Prefer: odata.include-annotations="*"include le annotazioni di formattazione, ad esempio le etichette dei choice/lookup (@OData.Community.Display.V1.FormattedValue).Prefer: odata.maxpagesize=<n>imposta la dimensione di pagina.
$batch, function e action
POST /$batch raggruppa più operazioni in una singola richiesta HTTP multipart. Le operazioni di modifica dentro un changeset sono transazionali (atomiche: o tutte o nessuna), mentre le GET vanno fuori dal changeset. È il meccanismo giusto per bulk load e per rispettare i limiti di throughput.
Dataverse espone anche logica applicativa:
- Function (
GET): operazioni di sola lettura senza side-effect, es.WhoAmI(),RetrieveTotalRecordCount. - Action (
POST): operazioni con side-effect, es.Merge,WinOpportunity, oppure custom API/custom action definite dal maker.
Web API vs Organization Service SDK
L’Organization Service (SDK .NET, IOrganizationService, ServiceClient del pacchetto Microsoft.PowerPlatform.Dataverse.Client) usa un contratto a messaggi (Create, Retrieve, Execute, QueryExpression/FetchXML). Scegli l’SDK quando:
- l’integrazione è in .NET e vuoi tipizzazione forte (early-bound), gestione robusta di paging/retry già incapsulata e riuso di plugin/logica server-side;
- servono query complesse con FetchXML (aggregazioni, link-entity avanzati).
Scegli la Web API quando il client non è .NET (JavaScript, Python, Java), quando integri con logic app/servizi HTTP, o dentro Power Apps/Power Automate. Concettualmente entrambe colpiscono lo stesso motore e gli stessi service protection limit; cambia solo il contratto (REST/OData vs SOAP/messaggi).
Trappole tipiche d’esame
- Service protection limit → 429: un’integrazione che invia troppe richieste in poco tempo (o supera i limiti per utente/connessione) riceve HTTP 429 Too Many Requests con header
Retry-After. La risposta corretta è implementare retry con backoff rispettandoRetry-After, non aumentare i thread né ignorare l’errore. - Paging su grandi result set: quando i risultati eccedono la pagina, la risposta contiene
@odata.nextLink. Devi seguire quel link così com’è (contiene il paging cookie); NON ricostruire l’URL con$skipmanuale. Per fissare la dimensione usaPrefer: odata.maxpagesize. - Alternate key negli URL: per referenziare un record tramite chiave alternativa (es. codice esterno) l’URL corretto è
/accounts(accountnumber='ACC-100'), non/accounts?$filter=.... Richiede che una alternate key sia definita sulla tabella; con chiave composta si passano tutte le colonne. - return=representation: se lo scenario chiede di ottenere il record appena creato/aggiornato in una sola chiamata, la risposta è l’header
Prefer: return=representation, non una seconda GET. - Transazionalità in $batch: per rendere atomiche più modifiche vanno racchiuse in un changeset dentro
$batch; operazioni fuori dal changeset non sono transazionali.