Custom API & Data Providers¶
Custom APIs¶
DGT-SRV-120 — Prefer Custom APIs over classic unbound/bound Actions for new server-side operations
callable from client code, Power Automate, or other plugins — they have first-class typed
request/response parameters and show up properly in the Dataverse Web API metadata.
A Custom API has two parts in Dataverse customization (the table/message definition, its
request/response parameters — see Customizing for that side) and
one part in code: the handler plugin, registered via
[CustomApiRegistration]:
[CustomApiRegistration("dgt_calc_vacations")]
public class CalcVacationsPlugin : Executor
{
protected override ExecutionResult Execute()
{
GetInputParameter("StartDate", out DateTime startDate);
GetInputParameter("EndDate", out DateTime endDate);
var vacationDays = CalculateVacationDays(startDate, endDate);
SetOutputParameter("VacationDays", vacationDays);
return ExecutionResult.Ok;
}
private static int CalculateVacationDays(DateTime start, DateTime end) =>
// ...
0;
}
GetInputParameter/SetOutputParameter come from Executor — there's
no need to reach into IServiceProvider/IPluginExecutionContext directly for a Custom API
handler any more than for a classic plugin.
Custom Data Providers (virtual tables)¶
For virtual table data sources, implement one handler per CRUD/retrieve event and register each
with [CustomDataProviderRegistration]:
[CustomDataProviderRegistration("dgt_virtual_table", DataProviderEvent.Retrieve)]
[CustomDataProviderRegistration("dgt_virtual_table", DataProviderEvent.RetrieveMultiple)]
public class VirtualTableReadHandler : Executor
{
protected override ExecutionResult Execute()
{
if (Query(out QueryExpression query, out var columnSet))
{
// build and return the EntityCollection / Entity for RetrieveMultiple / Retrieve
}
return ExecutionResult.Ok;
}
}
Executor.Query(...) (see DIGITALL Assembly) handles the
QueryExpression/QueryByAttribute/FetchExpression overloads the data provider pipeline can
hand you, including column set inference from a FetchXml query — you don't need to branch on
query type by hand before extracting columns.
Managed Identity¶
If a Custom API or plugin needs to call out to another Azure service using a managed identity
rather than a stored secret, mark the assembly with
[ManagedIdentityRegistration]
— but remember that attribute only covers the Dataverse-side registration; the Azure-side
managed identity and assembly signing are separate, manual setup steps.