Sam January 26, 2026 0

When working with Dataverse data in C#, QueryExpression is one of the most commonly used APIs, especially in plugins and server-side logic. While basic queries are straightforward, real-world scenarios often require joins, nested joins, and retrieving data from related tables.

In this post, we’ll focus on QueryExpression with LinkEntity, covering:

  • How joins work conceptually
  • Using LinkEntity
  • Nested LinkEntity (joins on joins)
  • Retrieving columns from linked entities
  • Common mistakes and best practices

What Is QueryExpression?

QueryExpression is a C# object-based query API provided by the Dataverse SDK.
It allows developers to retrieve data programmatically using structured objects instead of XML or UI-based queries.

QueryExpression is commonly used in:

  • Plugins
  • Custom workflow activities
  • SDK-based C# applications
  • Server-side integrations

Unlike Advanced Search or FetchXML, QueryExpression is code-only.

Understanding Joins in QueryExpression (Quick Concept)

In QueryExpression, a LinkEntity represents a join between two Dataverse tables.

The JoinOperator defines how records are matched:

  • Inner Join → Returns records only when related records exist
  • Left Outer Join → Returns all primary records, even if related records do not exist

In most plugin scenarios, LeftOuter is preferred to avoid unintentionally excluding records.

Basic QueryExpression

var query = new QueryExpression("account")
{
    ColumnSet = new ColumnSet("name", "accountnumber")
};

query.Criteria.AddCondition(
    "statecode",
    ConditionOperator.Equal,
    0
);

var results = service.RetrieveMultiple(query);

This retrieves all active Accounts with selected columns.

Using LinkEntity (Joining Related Tables)

Real-world data usually spans multiple tables.
To retrieve related data, QueryExpression uses LinkEntity.

Example Scenario

Retrieve Accounts along with their Primary Contact details.

Example: Account → Contact (Single LinkEntity)

var query = new QueryExpression("account")
{
    ColumnSet = new ColumnSet("name")
};

var contactLink = query.AddLink(
    "contact",
    "primarycontactid",
    "contactid",
    JoinOperator.LeftOuter
);

contactLink.EntityAlias = "primarycontact";
contactLink.Columns = new ColumnSet("fullname", "emailaddress1");

var results = service.RetrieveMultiple(query);

This joins:

  • Account → Contact
  • Retrieves selected contact columns

Accessing Columns from Linked Entities (Important)

Columns from linked entities are returned as AliasedValue.

foreach (var entity in results.Entities)
{
    var accountName = entity.GetAttributeValue<string>("name");

    var contactName = entity.GetAttributeValue<AliasedValue>(
        "primarycontact.fullname"
    )?.Value as string;
}

Common mistake:
Forgetting EntityAlias or not using AliasedValue.

Adding Filters on a Linked Entity

You can apply filters directly on the linked table.

contactLink.LinkCriteria.AddCondition(
    "emailaddress1",
    ConditionOperator.NotNull
);

This retrieves only accounts where the primary contact has an email address.

Nested LinkEntity (LinkEntity → LinkEntity)

In complex scenarios, you may need joins on joins.

Example Scenario

Retrieve Accounts → Primary Contact → Contact’s Parent Account

Example: Nested LinkEntity

var query = new QueryExpression("account")
{
    ColumnSet = new ColumnSet("name")
};

var contactLink = query.AddLink(
    "contact",
    "primarycontactid",
    "contactid",
    JoinOperator.LeftOuter
);
contactLink.EntityAlias = "pc";
contactLink.Columns = new ColumnSet("fullname");

var parentAccountLink = contactLink.AddLink(
    "account",
    "parentcustomerid",
    "accountid",
    JoinOperator.LeftOuter
);
parentAccountLink.EntityAlias = "parentacc";
parentAccountLink.Columns = new ColumnSet("name");

var results = service.RetrieveMultiple(query);

Reading Data from Nested Linked Entities

var parentAccountName = entity.GetAttributeValue<AliasedValue>(
    "parentacc.name"
)?.Value as string;

Each level of join:

  • Requires its own alias
  • Returns data as AliasedValue

Important Rules to Remember

  • Always set EntityAlias
  • Always use AliasedValue for linked entity columns
  • Prefer LeftOuter unless inner join is required
  • Retrieve only required columns
  • Avoid unnecessary joins for performance reasons

Conclusion

QueryExpression becomes truly powerful once you understand joins and LinkEntity usage.

With the right structure, you can:

  • Retrieve complex relational data
  • Keep logic inside C# code
  • Avoid multiple Dataverse calls

Mastering LinkEntity and nested joins is essential for real-world Dynamics 365 development.

In the next post, we’ll build on this foundation by covering paging, ordering, and performance considerations in QueryExpression.

Category: