Two APIs support file-based integration scenarios: the Data management framework’s package API and the recurring integrations API
Below are the sample C# snippets to be used for data Data management framework’s package API
data management project should be in place before hand
Main program class
Program
{
static async Task Main(string[] args)
{
// Define values once
string jobNameExport = "Cust Group DMF Export"; // Must match the data project in D365
string jobNameImport = "Cust Group DMF Import"; // Must match the data project in D365
string packageNameExport = "ExportedCustGroupPackage";
//string legalEntityExport = "usmf";
string legalEntityExport = "usmf";
string legalEntityImport = "usmf";
string filePathExport = @"C:\Users\source\repos\AXPDataManagementAPIDemo\AXPDataManagementAPIDemo\Export";
string filePathImport = @"C:\Users\source\repos\AXPDataManagementAPIDemo\AXPDataManagementAPIDemo\Import";
string fileNameExport = "CustGroupExport";
string fileNameImport = "CustGroupImport";
// Ensure directory exists
if (!Directory.Exists(filePathExport))
Directory.CreateDirectory(filePathExport);
// Ensure directory exists
if (!Directory.Exists(filePathImport))
Directory.CreateDirectory(filePathImport);
// Menu loop
while (true)
{
Console.WriteLine("\n=== DMF Console ===");
Console.WriteLine("1. Export Data");
Console.WriteLine("2. Import Data");
Console.WriteLine("0. Exit");
Console.Write("Select an option: ");
string input = Console.ReadLine();
switch (input)
{
case "1":
Console.WriteLine("\n--- Starting Export ---\n");
await DMFManager.Export(jobNameExport, packageNameExport, legalEntityExport, filePathExport, fileNameExport);
Console.WriteLine("\n Export process completed.");
break;
case "2":
Console.WriteLine("\n--- Starting Import ---\n");
await DMFMana class ger.Import(jobNameImport, legalEntityImport, filePathImport, fileNameImport);
Console.WriteLine("\n Import process completed.");
break;
case "0":
Console.WriteLine("Exiting...");
return;
default:
Console.WriteLine("Invalid selection. Try again.");
break;
}
}
}
}
contracts
public class DMFExport
{
[JsonProperty("definitionGroupId")]
public string DefinitionGroupId { get; set; }
[JsonProperty("packageName")]
public string PackageName { get; set; }
[JsonProperty("executionId")]
public string ExecutionId { get; set; }
[JsonProperty("reExecute")]
public bool ReExecute { get; set; }
[JsonProperty("legalEntityId")]
public string LegalEntityId { get; set; }
}
public class DMFImport
{
[JsonProperty("packageUrl")]
public string PackageUrl { get; set; }
[JsonProperty("definitionGroupId")]
public string DefinitionGroupId { get; set; }
[JsonProperty("executionId")]
public string ExecutionId { get; set; } = string.Empty;
[JsonProperty("overwrite")]
public bool Overwrite { get; set; }
[JsonProperty("execute")]
public bool Execute { get; set; }
[JsonProperty("legalEntityId")]
public string LegalEntityId { get; set; }
}
status check call
public class DMFExportSummary
{
[JsonProperty("executionId")]
public string ExecutionId { get; set; }
}
Authentications
public class OAuth2TokenService
{
private readonly SandboxEnvConfig _config;
private readonly HttpClient _httpClient;
public OAuth2TokenService(SandboxEnvConfig config)
{
_config = config;
_httpClient = new HttpClient();
}
public string Resource => _config.Resource;
public string GetAccessToken()
{
var tokenEndpoint = $"https://login.microsoftonline.com/{_config.TenantId}//oauth2/token";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", _config.ClientId),
new KeyValuePair<string, string>("client_secret", _config.ClientSecret),
new KeyValuePair<string, string>("resource", _config.Resource)
});
var response = _httpClient.PostAsync(tokenEndpoint, content).Result;
if (!response.IsSuccessStatusCode)
{
var error = response.Content.ReadAsStringAsync().Result;
throw new Exception($"Token request failed: {error}");
}
var json = response.Content.ReadAsStringAsync().Result;
using var doc = JsonDocument.Parse(json);
return doc.RootElement.GetProperty("access_token").GetString();
}
}
Manager
class DMFManager
{
static string downloadUrl = string.Empty;
static string aadResource = string.Empty;
/// <summary>
/// Retrieves an authentication header from the service.
/// </summary>
/// <returns>The authentication header for the Web API call.</returns>
private static string GetAuthenticationHeader()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddUserSecrets<Program>();
var configuration = builder.Build();
var config = configuration.GetSection("SandboxEnvConfig").Get<SandboxEnvConfig>();
var oauthService = new OAuth2TokenService(config);
try
{
var token = oauthService.GetAccessToken();
Console.WriteLine("Access Token Acquired ");
aadResource = oauthService.Resource; // capture it from config
return token;
}
catch (Exception ex)
{
Console.WriteLine(" Failed to get token:");
Console.WriteLine(ex.Message);
return null;
}
}
public static async Task<string> ImportFromPackageAsync(HttpClient client, string blobUrl, string jobName, string legalEntity)
{
var payload = new DMFImport
{
PackageUrl = blobUrl,
DefinitionGroupId = jobName,
LegalEntityId = legalEntity,
Overwrite = true,
Execute = true
};
var content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json");
var response = await client.PostAsync("/data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.ImportFromPackage", content);
var result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new Exception($"Import failed: {result}");
var parsed = JObject.Parse(result);
var value = parsed["value"]?.ToString();
if (string.IsNullOrWhiteSpace(value))
throw new Exception("No executionId returned from ImportFromPackage.");
Console.WriteLine("Execution ID retrieved: " + value);
return value;
}
// Setup Step
// - Create an export project within Dynamics called ExportVendors in company USMF before you run the following code
// - It can of any data format XML and can include any number of data entities
// 1. Initiate export of a data project to create a data package within Dynamics 365 for Operations
public static async Task Import(string jobName, string legalEntity, string filePath, string fileName)
{
string authHeader = GetAuthenticationHeader();
using HttpClient client = new HttpClient();
client.BaseAddress = new Uri(aadResource);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authHeader);
// 1. Get Azure Blob Write URL
Console.WriteLine(" Getting Azure Blob upload URL...");
var uploadRequest = new { uniqueFileName = fileName + ".zip" };
var stringPayload = JsonConvert.SerializeObject(uploadRequest);
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
var uploadUrlResponse = await client.PostAsync(
"/data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetAzureWriteUrl", httpContent);
var uploadUrlContent = await uploadUrlResponse.Content.ReadAsStringAsync();
if (!uploadUrlResponse.IsSuccessStatusCode)
{
Console.WriteLine("Failed to get blob URL:");
Console.WriteLine(uploadUrlContent);
return;
}
// Parse BlobUrl from nested string in "value"
string rawValue = JObject.Parse(uploadUrlContent)["value"]?.ToString();
if (string.IsNullOrWhiteSpace(rawValue))
{
Console.WriteLine("Blob URL response is empty.");
return;
}
JObject blobJson = JObject.Parse(rawValue);
string blobUrl = blobJson["BlobUrl"]?.ToString();
if (string.IsNullOrWhiteSpace(blobUrl))
{
Console.WriteLine("BlobUrl' not found in Azure response.");
return;
}
Console.WriteLine("Blob URL acquired:\n" + blobUrl);
// 2. Upload the ZIP file
Console.WriteLine("Uploading the data package...");
byte[] zipBytes = File.ReadAllBytes(Path.Combine(filePath, fileName + ".zip"));
using (var blobHttpClient = new HttpClient())
{
var blobContent = new ByteArrayContent(zipBytes);
blobContent.Headers.Clear(); // clearing the header here so the SAS token can authenticate the blob itself, the local auth doesn't interfere
blobContent.Headers.Add("x-ms-blob-type", "BlockBlob");
var blobUploadResponse = await blobHttpClient.PutAsync(blobUrl, blobContent);
if (blobUploadResponse.StatusCode != System.Net.HttpStatusCode.Created)
{
Console.WriteLine("Failed to upload package (expected 201 Created):");
Console.WriteLine(await blobUploadResponse.Content.ReadAsStringAsync());
return;
}
}
Console.WriteLine("Package uploaded successfully (201 Created).");
// 3. Trigger import
Console.WriteLine("Initiating import of data project...");
string execId;
try
{
execId = await ImportFromPackageAsync(client, blobUrl, jobName, legalEntity);
if (string.IsNullOrWhiteSpace(execId))
{
Console.WriteLine("No executionId returned from ImportFromPackage.");
return;
}
}
catch (Exception ex)
{
Console.WriteLine("ImportFromPackage failed:");
Console.WriteLine(ex.Message);
return;
}
Console.WriteLine($"Import started with Execution ID: {execId}");
// 4. Pull status
int maxLoop = 15;
string outPut = string.Empty;
do
{
Console.WriteLine("Checking import status...");
Thread.Sleep(5000);
maxLoop--;
if (maxLoop <= 0)
{
Console.WriteLine("Status check timed out.");
break;
}
stringPayload = JsonConvert.SerializeObject(new { executionId = execId });
httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
var statusResponse = await client.PostAsync(
"/data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetExecutionSummaryStatus", httpContent);
var statusContent = await statusResponse.Content.ReadAsStringAsync();
var parsedResult = JObject.Parse(statusContent);
var valueToken = parsedResult["value"];
if (valueToken == null)
{
Console.WriteLine("Status response missing 'value':");
Console.WriteLine(statusContent);
return;
}
outPut = valueToken.ToString();
Console.WriteLine($"Import Status: {outPut}");
} while (outPut == "NotRun" || outPut == "Executing");
if (outPut != "Succeeded" && outPut != "PartiallySucceeded")
{
Console.WriteLine("Import job failed with status: " + outPut);
return;
}
Console.WriteLine("Import job completed successfully.");
}
public static async Task Export(string jobName, string packageName, string legalEntity, string filePath, string fileName)
{
string authHeader = GetAuthenticationHeader();
using HttpClient client = new HttpClient();
client.BaseAddress = new Uri(aadResource);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authHeader);
//Initiate the Export
string execytionID = Guid.NewGuid().ToString();
var payload = new DMFExport()
{
DefinitionGroupId = jobName,
PackageName = packageName,
ExecutionId = execytionID,
ReExecute = true,
LegalEntityId = legalEntity
};
Console.WriteLine("Initiating export of a data project...");
var stringPayload = JsonConvert.SerializeObject(payload);
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
var result = client.PostAsync("/data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.ExportToPackage", httpContent).Result;
string resultContent = await result.Content.ReadAsStringAsync();
JObject joResponse = JObject.Parse(resultContent);
string outPut = string.Empty;
if (result.StatusCode == System.Net.HttpStatusCode.OK)
{
Console.WriteLine("Initiating export of a data project...Complete");
int maxLoop = 15;
do
{
Console.WriteLine("Waiting for package to execution to complete");
Thread.Sleep(5000);
maxLoop--;
if (maxLoop <= 0)
{
break;
}
Console.WriteLine("Checking status...");
stringPayload = JsonConvert.SerializeObject(new DMFExportSummary() { ExecutionId = execytionID });
httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
result = client.PostAsync("/data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetExecutionSummaryStatus", httpContent).Result;
resultContent = await result.Content.ReadAsStringAsync();
outPut = JObject.Parse(resultContent).GetValue("value").ToString();
Console.WriteLine("Status of export is " + outPut);
}
while (outPut == "NotRun" || outPut == "Executing");
if (outPut != "Succeeded" && outPut != "PartiallySucceeded")
{
throw new Exception("Operation Failed");
}
else
{
// 3. Get downloable Url to download the package
stringPayload = JsonConvert.SerializeObject(new DMFExportSummary() { ExecutionId = execytionID });
httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
result = client.PostAsync("/data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetExportedPackageUrl", httpContent).Result;
resultContent = await result.Content.ReadAsStringAsync();
downloadUrl = JObject.Parse(resultContent).GetValue("value").ToString();
}
// Download the file from Url to a local folder
Console.WriteLine("Downloading the file ...");
var blob = new CloudBlockBlob(new Uri(downloadUrl));
blob.DownloadToFile(Path.Combine(filePath, fileName + ".zip"), System.IO.FileMode.Create);
Console.WriteLine("Downloading the file ...Complete");
}
else
{
Console.WriteLine("Initiating export of a data project...Failed");
}
}
}