Skip to content

FetchXML Generator

Peter McDonald edited this page Aug 12, 2026 · 2 revisions

FetchXML Generator

Build, run and edit FetchXML where it already lives — in your code. There is no new file type to adopt: the extension finds the FetchXML in your C#, TypeScript or JavaScript, offers to run it against your environment, and opens a visual generator that writes your edits back into the same string literal.

Every screenshot below is from the automated end-to-end suite driving the real UI. (The environment name and URL are blanked out; you will see your own.)

Preview feature. The query tools ship behind the dataverse-powertools.previewFeatures setting, off by default. Tick Preview features in the panel footer to switch them on.

Why in the code, and not a .fetchxml file

A separate file would have to be copied into your source to be any use, and then the two would drift. Working on the literal means what you test is what ships — including the placeholders. A query like this needs no special treatment:

var fetchXml = $@"<fetch top='50'>
  <entity name='account'>
    <attribute name='name' />
    <filter type='and'>
      <condition attribute='accountid' operator='eq' value='{accountId}' />
    </filter>
  </entity>
</fetch>";
return service.RetrieveMultiple(new FetchExpression(fetchXml));

1. The CodeLens finds it

Open a file with FetchXML in it and a lens appears above the literal: Run and Edit in generator.

The Run and Edit in generator CodeLens above FetchXML in a C# file

It works the same on a TypeScript template literal, a +-concatenated chain, a C# verbatim, interpolated or raw string. Where a query has a problem, the lens says so — here an interpolated value dropped straight into an XML attribute, which breaks the query (or worse) the moment the value contains a quote or an &:

The issues lens on a TypeScript template literal with an unescaped interpolated value

2. Run it, and answer for the placeholders

Run executes the query against your connected environment. Anything your code interpolates — {accountId} above — becomes a placeholder, and the extension asks for a value before it runs, so a query full of variables is still testable:

The prompt asking for the accountId placeholder value before the query runs

The prompt knows what the value should look like: the type is inferred from the column being compared, so a Uniqueidentifier column rejects something that is not a GUID before a round trip is wasted.

3. Read the results

Rows come back in a results view beside your code, with the environment and the identity the query ran as — which matters, because your query runs there as you, while in production it runs as the calling user, so row-level security and eq-userid can resolve differently.

The results view with the row count and the environment the query ran against

Copy as CSV, Copy as JSON and Copy FetchXML are along the bottom.

4. Edit it in the generator

Edit in generator opens the query as a tree with a properties panel for whatever you select, the live FetchXML underneath, and your placeholders listed as parameters:

The FetchXML Generator: the query tree, the properties panel, the FetchXML text and the parameters

  • The tree is your query — entity, attributes, filters, conditions, links. Add and remove nodes from the toolbar under it.
  • The properties panel offers the attributes that element actually supports, with the meaning spelled out ("Maximum rows. Cannot be combined with paging."), so you are not guessing at FetchXML's grammar.
  • Metadata is lazy. Tables and columns load when you need them and are cached for the session; Reload metadata clears that cache when you have just changed the schema.
  • Parameters are the placeholders taken from your code — the generator names them after your own variables rather than inventing its own.

Run runs the edited query without touching your file. Save to code writes it back.

5. Save to code

The write-back is deliberately careful, because it is editing your source:

  • only the string literal changes — the rest of the file, including the surrounding code and formatting, is left exactly as it was;
  • your placeholders are put back as the code expressions they came from, not as literal @tokens;
  • the result is re-parsed and re-read to confirm what landed, and a no-op edit writes nothing at all.

Re-open the lens afterwards and the generator reads the same query back — the round trip is stable, and that is covered by the end-to-end suite rather than asserted here.

Where FetchXML is used, and what each consumer supports

Consumer How it takes FetchXML Notes
Plug-in / workflow (SDK) new FetchExpression(xml) Full FetchXML, including aggregates and link-entity
Web resource / PCF Xrm.WebApi.retrieveMultipleRecords(entity, "?fetchXml=" + encodeURIComponent(xml)) Must be URI-encoded; paging via the platform's own cookie handling
Saved view The view's fetchxml column No parameters — a view cannot prompt
Lookup filter A <filter> fragment only Not a whole <fetch>
Portals / Power Pages Liquid {% fetchxml %} Liquid, not C#/TS interpolation

The generator flags what a given consumer cannot do, so an aggregate that a lookup filter would reject is caught while you are writing it.

Troubleshooting

No lens appears. Tick Preview features in the panel footer. The lens also needs the text to actually parse as FetchXML — a malformed fragment reports an issue instead.

"Connect to Dataverse first." Running a query needs a live connection; the generator itself, and editing, do not.

Metadata looks stale. Reload metadata in the generator's toolbar; the cache is per session.

The prompt rejects my value. The type comes from the column you are comparing — a GUID column wants a GUID, a DateTime wants a date. Check the condition's attribute.

See also

  • Plugins — where an SDK FetchExpression runs
  • Web ResourcesXrm.WebApi and the typings that go with it

Clone this wiki locally