-
Notifications
You must be signed in to change notification settings - Fork 2
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.previewFeaturessetting, off by default. Tick Preview features in the panel footer to switch them on.
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));Open a file with FetchXML in it and a lens appears above the literal: Run and Edit in generator.

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 &:

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 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.
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.

Copy as CSV, Copy as JSON and Copy FetchXML are along the bottom.
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 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.
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.
| 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.
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.
-
Plugins — where an SDK
FetchExpressionruns -
Web Resources —
Xrm.WebApiand the typings that go with it