Image Preview
1 / 1
HomeLearnDynamic Entity Routing in Power Automate: Update Multiple Dataverse Tables Without Hardcoding
🔥 AdvancedDataversePower Automate11 min readOctober 2025

Dynamic Entity Routing in Power Automate: Update Multiple Dataverse Tables Without Hardcoding

Learn how to build dynamic Power Automate flows that route updates to different Dataverse tables without hardcoding table names. This guide covers extracting entity metadata from triggers, pluralising entity logical names, using Switch controls for routing, and handling polymorphic lookups to create flexible, maintainable flows that work across multiple table types.

RL
Rob Lees
Founder & Principal Consultant
Share

Overview

Dynamic entity routing enables Power Automate flows to interact with multiple Dataverse tables through a single flow design. By extracting entity metadata from trigger payloads, converting logical names to entity set names, and using Switch or lookup table patterns, you can build modular flows that determine the correct table at runtime without duplicating logic for each table type.

Prerequisites

  • Advanced Power Automate experience
  • Deep understanding of Dataverse tables and entity names
  • Familiarity with expressions and string manipulation
  • Experience with polymorphic lookups and entity metadata

Why Dynamic Entity Routing Matters

In scalable Power Platform solutions, it's common to build flows that interact with multiple Dataverse tables — especially when records share a common process such as approvals, logging, or status updates. These flows often need to determine the correct table at runtime, without hardcoding logic for each entity.

Typical use cases include:

  • Approval workflows triggered by various record types across departments
  • Audit logging that tracks changes across multiple entities
  • Status updates pushed from external systems into Dataverse
  • Generic notifications or escalations tied to different business objects

To keep flows modular and maintainable, you can dynamically extract the entity name, pluralise it, and route the update to the correct Dataverse table — all within a single Power Automate flow.

Benefits of dynamic routing:

  • Reusability – One flow handles multiple table types instead of separate flows per table
  • Maintainability – Update logic once rather than editing dozens of table-specific flows
  • Scalability – Easy to add new tables without creating new flows
  • Governance – Centralised logic reduces inconsistencies across tables
💡 Key Concept

Dataverse triggers and actions expose entity metadata in their outputs, including the entity logical name (singular: contact, account, opportunity) and entity set name (plural: contacts, accounts, opportunities). Dynamic routing extracts this metadata to determine which table to update at runtime, eliminating hardcoded table references and enabling one flow to handle multiple entity types.

Getting Table Name from Trigger Payload

Start by capturing the following fields from your trigger or input payload:

  • EntityLogicalName – e.g. contact, opportunity
  • EntityId – the GUID of the record
  • Type – optional, useful for polymorphic lookups

Use a Filter array or Parse JSON action to isolate these values. Then extract them with expressions like:

first(body('Filter_array_EntityLogicalName'))?['contact_opportunityid']
first(outputs('Filter_array_EntityId'))?['value']  (', '', ')', '', ')')

Example trigger output structure:

{
  "EntityLogicalName": "contact",
  "EntityId": "a7b3c5e2-1234-5678-90ab-cdef12345678",
  "Type": "account",
  "RecordData": {
    "firstname": "John",
    "lastname": "Smith"
  }
}

Extracting entity logical name:

// From trigger body
triggerBody()?['EntityLogicalName']

// From Parse JSON output
body('Parse_JSON')?['EntityLogicalName']

// From webhook payload
outputs('When_HTTP_request_received')?['body']?['EntityLogicalName']

If your payload uses type instead of value, adjust accordingly:

first(body('Filter_array_EntityType'))?['type']

Example: Extracting from Dataverse trigger

Start by operating the following fields from your trigger or input payload:

// When a row is added, modified, or deleted trigger
// Metadata available in trigger outputs

Compose: Entity Logical Name
outputs('When_a_row_is_modified')?['body']?['@odata.type']

// Returns: Microsoft.Dynamics.CRM.contact
// Extract just "contact" using replace and split

Compose: Clean Entity Name
last(split(outputs('Compose_Entity_Logical_Name'), '.'))

// Returns: contact

Alternative: Using custom webhook payloads

If triggering from external systems via HTTP request trigger:

// HTTP request payload schema
{
  "type": "object",
  "properties": {
    "EntityLogicalName": {"type": "string"},
    "EntityId": {"type": "string"},
    "UpdatedFields": {"type": "object"}
  }
}

// Access in flow
triggerBody()?['EntityLogicalName']
triggerBody()?['EntityId']
⚠️ Metadata Availability

Not all triggers expose entity metadata in the same format. Dataverse triggers include @odata.type annotations, while HTTP request triggers depend on payload design. PowerApps triggers require passing entity name as parameter. Always examine trigger outputs in flow run history to identify where entity metadata is located before building extraction logic.

Converting Logical Name to Entity Set Name

Dataverse uses pluralised Entity Set Names in its Web API and connector actions. These names aren't always intuitive, so here's a basic expression to handle common pluralisation rules:

if(endsWith(outputs('Compose_EntityLogicalName'), 'y'),
  concat(substring(outputs('Compose_EntityLogicalName'), 0, sub(length(outputs('Compose_EntityLogicalName')), 1)), 'ies'),
  if(or(endsWith(outputs('Compose_EntityLogicalName'), 's'), endsWith(outputs('Compose_EntityLogicalName'), 'x')), 
    concat(outputs('Compose_EntityLogicalName'), 'es'), 
    concat(outputs('Compose_EntityLogicalName'), 's')
  )
)

This handles most cases. For irregular plurals, consider using a Dataverse lookup table to store overrides.

Pluralisation rules explained:

Rule Logical Name Entity Set Name
Ends with 'y' → replace with 'ies' opportunity opportunities
Ends with 's' or 'x' → add 'es' address addresses
Default → add 's' contact contacts
Irregular (manual mapping) systemuser systemusers

Breaking down the pluralisation expression:

// Step 1: Check if ends with 'y'
if(endsWith(outputs('Compose_EntityLogicalName'), 'y'),
  
  // Yes: Remove 'y' and add 'ies'
  concat(
    substring(
      outputs('Compose_EntityLogicalName'), 
      0, 
      sub(length(outputs('Compose_EntityLogicalName')), 1)
    ), 
    'ies'
  ),
  
  // No: Check if ends with 's' or 'x'
  if(
    or(
      endsWith(outputs('Compose_EntityLogicalName'), 's'), 
      endsWith(outputs('Compose_EntityLogicalName'), 'x')
    ), 
    
    // Yes: Add 'es'
    concat(outputs('Compose_EntityLogicalName'), 'es'), 
    
    // No: Just add 's'
    concat(outputs('Compose_EntityLogicalName'), 's')
  )
)

Handling irregular plurals with lookup table:

For maximum flexibility, store these mappings in a Dataverse table with columns for LogicalName and EntitySetName. This allows you to override edge cases and future-proof your flow.

// Lookup mapping table
List rows:
  Table: Entity Name Mappings
  Filter: LogicalName eq '@{variables('varEntityLogicalName')}'

// If match found, use mapped value
if(
  greater(length(outputs('List_rows')?['body/value']), 0),
  outputs('List_rows')?['body/value']?[0]?['EntitySetName'],
  // Else use pluralisation expression as fallback
  [pluralisation expression here]
)

Example mappings table:

LogicalName EntitySetName Notes
systemuser systemusers Irregular - doesn't follow normal rules
incident incidents Case table - standard pluralisation
cr_customentity cr_customentities Custom table with publisher prefix
💡 Pro Tip

Build the lookup table approach from the start, even if you only have a few tables initially. Maintaining pluralisation logic in expressions becomes unwieldy as you add more tables. A Dataverse lookup table is easier to update, allows non-developers to manage mappings, and eliminates complex nested if() expressions that are hard to maintain and troubleshoot.

Using Switch Control for Table Selection

Once you've pluralised the table name, use a Switch control or a lookup table to route the update. This keeps your flow modular and avoids hardcoding logic.

Example mapping:

  • contact → contacts
  • opportunity → opportunities
  • clientreview → clientreviews

For maximum flexibility, store these mappings in a Dataverse table with columns for LogicalName and EntitySetName. This allows you to override edge cases and future-proof your flow.

Method 1: Switch control

Action: Switch

On: outputs('Compose_Entity_Set_Name')

Case: contacts
  Update a row:
    Table: Contacts
    Row ID: outputs('Compose_Entity_ID')
    [field updates]

Case: opportunities  
  Update a row:
    Table: Opportunities
    Row ID: outputs('Compose_Entity_ID')
    [field updates]

Case: incidents
  Update a row:
    Table: Cases
    Row ID: outputs('Compose_Entity_ID')
    [field updates]

Default:
  // Log error - unrecognised entity type

Method 2: Direct HTTP request (most flexible)

Action: HTTP with Azure AD

Method: PATCH
Base Resource URL: https://[org].crm.dynamics.com/
Azure AD Resource URI: https://[org].crm.dynamics.com/

URI: 
concat(
  'api/data/v9.2/',
  outputs('Compose_Entity_Set_Name'),
  '(',
  outputs('Compose_Entity_ID'),
  ')'
)

Headers:
{
  "Content-Type": "application/json"
}

Body:
{
  "statuscode": 2,
  "description": "Updated via dynamic routing"
}

This HTTP approach allows truly dynamic table selection without Switch cases — the table name is constructed at runtime in the URI.

Comparison of routing methods:

Method Pros Cons
Switch control Visual clarity, easy to debug, action-specific configuration Must add case for each new table, doesn't scale well
HTTP with Azure AD Fully dynamic, scales infinitely, no cases needed Requires Azure AD app registration, harder to debug, raw JSON body
Lookup table routing Non-developers can manage, data-driven configuration Complexity overhead, still needs Switch or HTTP

Example HTTP body for dynamic updates:

// Store update fields in variables or compose actions
{
  "@{variables('varFieldName1')}": "@{variables('varFieldValue1')}",
  "@{variables('varFieldName2')}": "@{variables('varFieldValue2')}",
  "modifiedon": "@{utcNow()}"
}

// Or use Parse JSON to build from trigger payload
@{body('Parse_JSON')?['UpdateFields']}
⚠️ Azure AD Configuration

The HTTP with Azure AD action requires an Azure AD app registration with Dataverse API permissions. Register an app in Azure Portal, grant user_impersonation permission to Dynamics CRM, and configure the connection in Power Automate. This setup enables authentication for dynamic HTTP requests to Dataverse Web API without hardcoded credentials.

Dynamic Routing with Multi-Table Lookups

In polymorphic scenarios, the type or EntityLogicalName becomes essential. Use it to:

  • Determine which Dataverse table to query or update
  • Log activity against the correct entity without redundant handling
  • Build scalable forms that allow tri-flow services and links

Example: Setting Regarding field dynamically

// Entity type from payload
Compose: Entity Type
triggerBody()?['RegardingEntityType']

Compose: Entity ID  
triggerBody()?['RegardingEntityID']

// Pluralise entity type
Compose: Entity Set Name
[pluralisation expression using varEntityType]

// Create activity with dynamic Regarding
Add a new row:
  Table: Emails
  Subject: "Follow up"
  Regarding: 
    concat(
      '/',
      outputs('Compose_Entity_Set_Name'),
      '(',
      outputs('Compose_Entity_ID'),
      ')'
    )

Example: Querying related records dynamically

// Get activities related to any entity type
List rows:
  Table: Activities
  Filter rows:
    _regardingobjectid_value eq @{variables('varEntityID')}
    and
    regardingobjecttypecode eq '@{variables('varEntityLogicalName')}'

// Returns activities for specified entity regardless of type

Example: Dynamic expansion queries

// Build expand query dynamically based on entity type
Compose: Expand Query
if(
  equals(variables('varEntityType'), 'account'),
  'parentaccountid($select=name)',
  if(
    equals(variables('varEntityType'), 'contact'),
    'parentcustomerid_account($select=name)',
    ''
  )
)

// Use in Get row
Get a row:
  Table: [Selected dynamically via Switch or HTTP]
  Row ID: variables('varEntityID')
  Expand Query: outputs('Compose_Expand_Query')

This approach supports modular architecture and aligns with governance-first design principles.

💡 Pro Tip

When building polymorphic lookup references dynamically, always validate that the entity type is in your allowed list before constructing the reference. Unknown or misspelled entity types cause InvalidLookupReference errors. Use a Condition to check if entity type matches expected values, and handle invalid types gracefully with error logging rather than letting the flow fail.

Design Considerations for Dynamic Routing

Best practices:

  • Validate entity names early – Check that extracted entity names are in your expected list before routing
  • Use lookup tables for mappings – Store logical name to entity set name mappings in Dataverse for easy maintenance
  • Log unknown entity types – When Switch default case or invalid entity is encountered, log to monitoring table
  • Centralise pluralisation logic – Create child flow or shared expression for entity name conversion
  • Document entity metadata sources – Clearly document where entity type information comes from in different scenarios

Common pitfalls:

  • Forgetting publisher prefixes – Custom tables include prefixes (cr_contact → cr_contacts) which pluralisation must handle
  • Assuming standard pluralisation – Many system tables have irregular plurals (systemuser → systemusers, not systemusers)
  • Hardcoding field names – Field names may differ across tables; dynamic routing works best with common fields
  • Insufficient error handling – Dynamic flows have more failure points; implement comprehensive try-catch patterns

Performance considerations:

  • Lookup table queries add latency — cache mappings in variables if possible
  • Switch controls with many cases can impact readability — consider HTTP approach for 10+ tables
  • HTTP requests to Dataverse Web API count against API limits — monitor usage

Governance and maintenance:

  • Document which flows use dynamic routing and which tables they support
  • Create Change Management process for adding new tables to dynamic flows
  • Test thoroughly when adding tables — ensure pluralisation and field mappings work
  • Use solutions to deploy dynamic flows and mapping tables together across environments

When NOT to use dynamic routing:

  • Table-specific business logic that differs significantly across entities
  • Performance-critical flows where Switch/HTTP overhead is unacceptable
  • Simple scenarios with only 2-3 tables where separate flows are clearer
  • When field names and structures differ completely across tables
⚠️ Testing Across Tables

Always test dynamic routing flows with data from EVERY supported table type before deploying to production. Test not just success cases but also missing data, null values, and invalid entity types. Create comprehensive test records covering edge cases for each table to ensure routing logic works reliably across all scenarios.

Building Scalable Dynamic Flows

By extracting and pluralising entity names dynamically, you can build flows that are scalable, governance-friendly, and easy to maintain. This technique empowers you to handle diverse record types without duplicating logic — ideal for consultancy-grade builds where flexibility and clarity are paramount.

Key takeaways:

  • Extract entity logical name from trigger or payload metadata
  • Convert logical name to entity set name using pluralisation rules or lookup table
  • Route updates using Switch control or HTTP with Azure AD for maximum flexibility
  • Handle polymorphic lookups by dynamically constructing entity references
  • Validate entity types early and log errors for unsupported tables

Next steps for implementation:

  1. Create entity name mapping table in Dataverse with LogicalName and EntitySetName columns
  2. Build child flow for pluralisation logic that other flows can call
  3. Design HTTP with Azure AD template for dynamic Dataverse updates
  4. Document supported entity types and required field mappings
  5. Create test scenarios covering all supported tables

Expand your dynamic routing capabilities by exploring:

  • Dynamic field mapping – Using configuration tables to map field names across different entity types
  • Multi-environment deployment – Managing entity name mappings across dev/test/prod environments
  • Error logging patterns – Building centralised error tracking for dynamic routing failures
  • Performance optimisation – Caching entity metadata to reduce lookup table queries
  • Child flow architecture – Designing reusable routing flows called by multiple parent flows
  • API limit management – Monitoring and throttling dynamic HTTP requests to Dataverse

The Microsoft Dataverse Web API entity types documentation provides comprehensive information on entity logical names, entity set names, and metadata structures for building advanced dynamic routing solutions in Power Automate.

Article Info
Advanced
For experienced Power Platform developers.
11 min read  ·  October 2025
Prerequisites
Advanced Power Automate experience
Deep understanding of Dataverse tables and entity names
Familiarity with expressions and string manipulation
Experience with polymorphic lookups and entity metadata

Need this built for your business?

We design and build production-grade Power Platform solutions for FM, Construction and Manufacturing businesses.

Book a Discovery Call →