如何在Dynamics 365中点击按钮调用C# Web API导出数据
Got it, let’s walk through exactly how to add a button in your Dynamics 365 instance that triggers your existing export API. I’ve tailored these steps to work with the code you already have, so let’s dive in:
Right now, your API runs locally in Visual Studio—you need to get it online somewhere Dynamics 365 can reach it. Azure App Service is a straightforward cloud option, but an internal server (on the same network as your Dynamics instance) works too. Before deploying, double-check:
- Your
CrmOnlineconnection string is correctly configured for the deployed environment (so the API can still talk to your Dynamics instance) - You’ve enabled HTTPS (Dynamics requires external services to use secure connections)
- Test the deployed API directly (with Postman or a browser) to confirm it generates and returns the Excel file successfully
Next, we’ll add a button to the Activity grid (or form) that users can click to trigger the export. Use the Power Apps Maker Portal for this:
- Open Power Apps Maker, select your Dynamics 365 environment
- Go to Solutions, open your target solution (or create a new one to keep changes organized)
- Find the Activity Pointer entity, then open its Command Bar (choose "Main Grid" if you want the button on the list view, or select a specific form if you want it there)
- Click New Command and set up the button:
- Label: Something clear like "Export Activities via API"
- Icon: Pick an appropriate one (the built-in Excel icon works great)
- Action: Select JavaScript—this is where we’ll write code to call your API
Paste this custom script into the JavaScript action field (replace the placeholder API URL with your deployed endpoint):
function callExportAPI() { // Replace with your deployed API's actual URL const apiUrl = "https://your-deployed-api.azurewebsites.net/api/ExportActivities"; // Show a loading indicator so users know something's happening Xrm.Utility.showProgressIndicator("Exporting activities..."); // Call the API (adjust method to POST if your API uses it) fetch(apiUrl, { method: "GET", headers: { "Content-Type": "application/json", // Add auth headers if your API needs them—e.g., API key or OAuth token // "Authorization": "Bearer " + Xrm.Utility.getGlobalContext().userSecurityToken } }) .then(response => response.blob()) .then(blob => { // Trigger the browser download for the Excel file const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement("a"); link.href = downloadUrl; link.download = "Activities.xlsx"; document.body.appendChild(link); link.click(); // Clean up and notify user window.URL.revokeObjectURL(downloadUrl); document.body.removeChild(link); Xrm.Utility.closeProgressIndicator(); Xrm.Navigation.openAlertDialog({ text: "Export completed successfully!" }); }) .catch(error => { Xrm.Utility.closeProgressIndicator(); Xrm.Navigation.openErrorDialog({ message: `Export failed: ${error.message}` }); console.error("API call error:", error); }); }
Optional: Pass Selected Records
If you want users to export only selected activities instead of the full set, modify the script to send selected record IDs to your API:
// Get selected records from the grid const selectedRecords = Xrm.Page.getControl("subgrid").getSelectedRecords(); const recordIds = selectedRecords.map(record => record.getId()); // Send IDs via POST to your API fetch(apiUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ recordIds: recordIds }) })
Don’t forget to update your API to accept and use these IDs in its FetchXml!
Your existing code is a console app—we need to turn it into an ASP.NET Core Web API so it can handle HTTP requests from Dynamics. Here’s the adjusted version:
using Microsoft.AspNetCore.Mvc; using Microsoft.Crm.Sdk.Messages; using Microsoft.Xrm.Client; using Microsoft.Xrm.Client.Services; using Microsoft.Xrm.Sdk; using System.Configuration; using System.IO; using System.Linq; namespace ActivityExportAPI.Controllers { [ApiController] [Route("api/[controller]")] public class ExportActivitiesController : ControllerBase { [HttpGet] public IActionResult ExportActivities() { try { var connection = CrmConnection.Parse(ConfigurationManager.ConnectionStrings["CrmOnline"].ConnectionString); using (var orgService = new OrganizationService(connection)) { var exportRequest = new OrganizationRequest("ExportToExcel"); exportRequest.Parameters = new ParameterCollection { {"View", new EntityReference("savedquery", new Guid("{00000000-0000-0000-00AA-000010001902}"))}, {"FetchXml", @"<?xml version='1.0'?> <fetch distinct='false' mapping='logical' output-format='xml-platform' version='1.0'> <entity name='activitypointer'> <attribute name='subject'/> <attribute name='ownerid'/> <attribute name='prioritycode'/> <attribute name='regardingobjectid'/> <attribute name='activitytypecode'/> <attribute name='statecode'/> <attribute name='scheduledstart'/> <attribute name='scheduledend'/> <attribute name='activityid'/> <attribute name='instancetypecode'/> <attribute name='community'/> <attribute name='senton'/> <attribute name='statuscode'/> <order descending='false' attribute='scheduledend'/> <filter type='and'> <condition attribute='actualdurationminutes' value='43800' operator='le'/> </filter> <link-entity name='systemuser' alias='activitypointerowningusersystemusersystemuserid' link-type='outer' visible='false' to='owninguser' from='systemuserid'> <attribute name='internalemailaddress'/> </link-entity> <link-entity name='email' alias='email_engagement' link-type='outer' visible='false' to='activityid' from='activityid'><attribute name='isemailfollowed'/> <attribute name='lastopenedtime'/> <attribute name='delayedemailsendtime'/> </link-entity> </entity> </fetch>"}, {"LayoutXml", @" <grid name='resultset' object='2' jump='fullname' select='1' icon='1' preview='1'> <row name='result' id='activitypointerid'> <cell name='activitytypecode' width='150' /> <cell name='statecode' width='112' /> <cell name='scheduledstart' width='110' /> <cell name='scheduledend' width='110' /> </row> </grid>"}, {"QueryApi", ""}, {"QueryParameters", new InputArgumentCollection()} }; var exportResponse = orgService.Execute(exportRequest); if (exportResponse.Results.Any()) { var excelBytes = exportResponse.Results["ExcelFile"] as byte[]; return File(excelBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Activities.xlsx"); } return BadRequest("No data available to export"); } } catch (FaultException<OrganizationServiceFault> ex) { return StatusCode(500, $"Dynamics API Error: {ex.Message}"); } } } }
Key changes:
- Converted to an ASP.NET Core controller that returns a
Fileresult (triggers browser download) - Removed console-specific code (like
Mainmethod andWindows.Formsreferences) - Ensured the connection string is pulled from the deployed app’s config
- Publish your updated Web API to your host
- Save and publish the custom button in Power Apps Maker
- Open Dynamics 365, navigate to the Activity list view, and click your new button—you should see the Excel file download automatically
- CORS Errors: If Dynamics can’t call your API, check your host’s CORS settings to allow your Dynamics instance URL (e.g.,
https://yourorg.crm.dynamics.com) - Authentication Issues: If your API needs secure access, add API key validation or integrate with Azure AD OAuth to accept Dynamics user tokens
- FetchXml Flexibility: To let users export their current view’s data, modify the JavaScript to pull the view’s FetchXml and send it to your API instead of using the fixed FetchXml in your code
内容的提问来源于stack exchange,提问作者Mohd Nazish

