L’interfaccia IOrganizationService

IOrganizationService è il contratto principale per interagire con Dataverse dal codice .NET, sia all’interno di un plug-in sia da un’applicazione esterna. Espone un insieme fisso di metodi che coprono l’intero ciclo CRUD e le operazioni sui metadati e sulle relazioni.

Le operazioni sui record

  • Create — inserisce una nuova Entity e restituisce il Guid del record creato.
  • Retrieve — legge un singolo record dato entityName, id e un ColumnSet che definisce le colonne da caricare (evita ColumnSet(true) per motivi di performance).
  • Update — aggiorna solo le colonne valorizzate nell’oggetto Entity (update parziale): non serve rileggere l’intero record.
  • Delete — elimina il record indicato.
  • Associate / Disassociate — creano o rimuovono relazioni tra record tramite un oggetto Relationship, tipicamente per le relazioni many-to-many.
  • Execute — il metodo più potente: esegue qualsiasi OrganizationRequest (es. WhoAmIRequest, ExecuteMultipleRequest, una Custom API o azione). Quando non esiste un metodo dedicato, si passa da Execute.

Le opzioni di query

Per RetrieveMultiple hai tre approcci:

  • QueryExpression — modello a oggetti fortemente tipizzato: ideale quando costruisci la query dinamicamente nel codice (filtri, LinkEntity, ordinamenti). Non supporta le aggregazioni.
  • FetchXML (FetchExpression) — query in formato XML: unica opzione per le aggregazioni (count, sum, group by) e per riusare query salvate o costruite nell’editor.
  • LINQ provider — tramite OrganizationServiceContext / CreateQuery<T>: più leggibile, ma viene tradotto internamente in QueryExpression, quindi non tutte le espressioni LINQ sono supportate.

Contesto di esecuzione nei plug-in

Dentro un plug-in non istanzi un client: ottieni il servizio dalla IOrganizationServiceFactory recuperata dal IServiceProvider.

var factory = (IOrganizationServiceFactory)serviceProvider
    .GetService(typeof(IOrganizationServiceFactory));
var service = factory.CreateOrganizationService(context.UserId);

Il Guid passato a CreateOrganizationService determina il contesto di sicurezza:

  • context.UserId — il servizio opera come l’utente sotto cui il plug-in è registrato per l’esecuzione: le operazioni rispettano i suoi security role.
  • context.InitiatingUserId — l’utente che ha effettivamente scatenato l’operazione.
  • null — il servizio opera come utente SYSTEM, con privilegi pieni che bypassano il controllo di sicurezza. Usalo con cautela, solo quando serve un’operazione privilegiata.

L’impersonation consiste nel passare l’ID di un altro utente: le operazioni vengono valutate con i privilegi di quell’utente. Perché sia consentita, il chiamante deve possedere il privilegio prvActOnBehalfOfAnotherUser (ruolo Delegate). Da client esterno l’equivalente è impostare la proprietà CallerId su ServiceClient.

Operazioni bulk e client esterni

Da un’applicazione esterna il client corrente è ServiceClient (namespace Microsoft.PowerPlatform.Dataverse.Client), autenticato via Microsoft Entra ID con una app registration (client secret o certificato); CrmServiceClient e OrganizationServiceProxy sono ormai legacy.

Per grandi volumi:

  • ExecuteMultipleRequest — raggruppa fino a 1000 richieste in una sola chiamata, riducendo i round-trip di rete. Le proprietà ContinueOnError e ReturnResponses ne controllano il comportamento; non è transazionale.
  • ExecuteTransactionRequest — esegue più richieste in un’unica transazione atomica (tutto o niente).

La differenza chiave: ExecuteMultiple ha senso dal client esterno, dove il costo dominante è la latenza di rete. Dentro un plug-in giri già in-process sul server, quindi il batching non porta benefici e va evitato; contano invece il limite dei 2 minuti di esecuzione e la profondità (Depth) delle chiamate ricorsive.

Trappole tipiche d’esame

  • Serve conteggiare o aggregare i record → usa FetchXML: QueryExpression e il LINQ provider non supportano le aggregazioni.
  • Plug-in che deve creare record che l’utente non potrebbe creareCreateOrganizationService(null) per girare come SYSTEM; se invece devi rispettare i permessi dell’utente, passa context.UserId.
  • Impersonation che fallisce con errore di privilegi → l’utente impersonato non ha i diritti sull’operazione (l’impersonation non aumenta i privilegi) oppure il chiamante non possiede prvActOnBehalfOfAnotherUser.
  • Migliaia di record da inserire da un job esternoExecuteMultipleRequest per ridurre i round-trip, non un ciclo di singole Create; ricorri a ExecuteTransactionRequest solo se serve atomicità.
  • Nuovo client per connettersi a DataverseServiceClient con autenticazione Microsoft Entra ID, non il deprecato CrmServiceClient.