Reading a Dataverse solution from the outside: solutioncomponents, ObjectTypeCodes and why flow names come back as GUIDs

The solution has 400 components and the API describes them as type 10080, type 29 and a list of GUIDs. Here is how to turn that into a table with names on it — including the two ids every cloud flow carries and the one that actually matches.

LogiSam··7 min read

A solution's contents are in solutioncomponents: one row per component with an objectid, a componenttype and a rootcomponentbehavior. Query it with _solutionid_value eq <id> and you get the list. Making it readable is the work.

Component types

Some codes are fixed across every organisation: 1 is a table, 29 is a workflow (cloud flow, desktop flow, business process flow — the category on the workflow row distinguishes them), 80 is a model-driven app, 300 is a canvas app, 10 is a relationship, 9 an option set, 61 a web resource. Codes of 10000 and above are custom tables and are specific to that organisation: type 10080 in one tenant is a different table from 10080 in another. Resolve them through EntityDefinitions?$select=ObjectTypeCode,LogicalName,DisplayName for that environment, not from a static list.

Resolving names

  • Tables: objectid is the MetadataId; read EntityDefinitions(<id>) for the logical and display names.
  • Cloud and desktop flows: objectid is a workflow id. Read the workflows table with $select=workflowid,workflowidunique,name,category,statecode,_ownerid_value and index it by both ids — different solutions record different ones, and a lookup on only one leaves half the flows as GUIDs.
  • Canvas apps: objectid is the canvasappid. The app's Flow-API-style id — the one the admin APIs use — is in its appopenuri, so keep both for joining to the rest of the inventory.
  • Model-driven apps: objectid is the appmoduleid, and the name is on appmodules.

Batching without tripping the URL limit

Filtering workflows by two hundred workflowid eq clauses exceeds the URL length Dataverse accepts. Either batch in groups of twenty-five, or — simpler and faster for anything above a few hundred flows — read the whole table once per environment and index it in memory. The whole-table read is one paginated request and answers every solution in that environment.

The second hop for flows

solutioncomponents sometimes records a flow by workflowidunique and sometimes by workflowid, depending on how it was added. The Flow API knows only workflowidunique. So a component that resolves to a workflow row still needs a second hop to the Flow API name before it can be matched to the flow in the inventory — and that hop is why an unindexed lookup makes every solution flow look like it belongs to no solution.

Warnings are data

Some reads will be refused: a table the account has no privilege on, an EntityDefinitions call for a component that was deleted after the solution recorded it. Do not swallow these. Show the count of components that could not be named and the reason, because "three components could not be read: privilege denied on msdyn_..." is an answer, and a silent GUID is not.

Dataverse solutioncomponentsDataverse Web APIObjectTypeCodePowerPlatformsolution components listworkflowidunique
Reading a Dataverse solution from the outside: solutioncomponents, ObjectTypeCodes and why flow names come back as GUIDs · Power Insights