Hi,
I want to create DLL which at first will get an AccessToken from Azure. Here are the steps:
1. Create C# project, there is these methods:
public async Task<string> GetAccessTokenAsync(string authUrl, string clientId, string clientSecret, string scope)
{
var client = new HttpClient();
var tokenRequest = new FormUrlEncodedContent(new[] {
new KeyValuePair < string, string > ("grant_type", "client_credentials"),
new KeyValuePair < string, string > ("client_id", clientId),
new KeyValuePair < string, string > ("client_secret", clientSecret),
new KeyValuePair < string, string > ("scope", scope)
});
for (int i = 1; i <= NumberOfRetries; ++i)
{
try
{
HttpResponseMessage response = await client.PostAsync(authUrl, tokenRequest);
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
dynamic jsonResponse = JsonConvert.DeserializeObject(content); //must declare using Newtonsoft.Json;
return jsonResponse.access_token;
}
catch (Exception ex)
{
if (i != NumberOfRetries)
{
await Task.Delay(DelayOnRetry);
}
else
{
LogError("GetAccessTokenAsync", ex);
string v = $"Error: {ex.Message}";
return v;
}
}
}
return null;
}
/// <summary>
/// 1. Avoid Asynchronous Methods in X++, X++ does not natively support asynchronous methods(async and await) and Task-based programming like C#.
/// 2. Sync Method (GetAccessToken): Uses task.Wait() and task.Result to synchronously wait for the async operation to complete.
/// </summary>
/// <param name="authUrl"></param>
/// <param name="clientId"></param>
/// <param name="clientSecret"></param>
/// <param name="scope"></param>
/// <returns></returns>
public string GetAccessToken(string authUrl, string clientId, string clientSecret, string scope)
{
try
{
// Blocking call to ensure the method is synchronous
var task = GetAccessTokenAsync(authUrl, clientId, clientSecret, scope);
task.Wait(); // Wait for the async task to complete
return task.Result; // Retrieve the result
}
catch (AggregateException aggEx)
{
// Handle potential AggregateException from task.Wait()
for (int i = 0; i < aggEx.InnerExceptions.Count; i++)
{
Exception ex = aggEx.InnerExceptions[i];
LogError("GetAccessToken", ex);
}
return $"Error: {aggEx.Message}";
}
catch (Exception ex)
{
// Handle any other exceptions
LogError("GetAccessToken", ex);
return $"Error: {ex.Message}";
}
}
}
2. Rebuild this class. The DLL file is saved in its default location <my project folder> / bin / debug
3. I have created X++ class to consume the DLL, have added the reference to the DLL (am not moving the DLL file, so it is still in the same path as in point 2). And inside this class, have this method:
class A_Integration_General
{
/// <summary>
/// Get Access Token
/// </summary>
/// <returns>Access Token as a string</returns>
public static System.String getAccessToken()
{
//SystemParameters systemParameters = SystemParameters::find();
System.String clientId = "d365fo.client";
System.String clientSecret = "ixxxxxxxxxxxxxxxxxxxxxx";
System.String scope = "xxxxxxAPI";
System.String IntegrationUrl = "https://xxxxxxxx/connect/token";
System.String accessToken;
// Instantiate the DLL object directly
A_Integration.A_ConnectionUtils connection = new A_Integration.A_ConnectionUtils();
// Call the method directly without using reflection
accessToken = connection.GetAccessToken(IntegrationUrl, clientId, clientSecret, scope);
return accessToken;
}
}
4. Create Runnable class for testing :
internal final class A_TestCallDLL
{
public static void main(Args _args)
{
System.String accessToken;
try
{
// Call the getAccessToken method from your custom class
accessToken = A_Integration_General::getAccessToken();
// Display the access token in the infolog
info(strFmt("Access Token: %1", accessToken));
}
catch (Exception::CLRError)
{
// Handle any CLR errors that occur during the call
info("An error occurred while retrieving the access token.");
throw error(AifUtil::getClrErrorMessage());
}
catch (Exception::Error)
{
// Handle any other X++ errors
info("An X++ error occurred.");
throw;
}
}
}
5. Build model. Succeeded with no error.
However, when calling this runnable class on frontend (D365 FO UI), I keep getting this error : Could not load type 'System.Threading.Tasks.Task`1' from assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
May I know how to resolve this ? I have limited knowledge regarding this, if I googled it is something about X++ didn't support Async, but I already tried to add the method to make it as sync with method "getAccessToken" in my C# DLL. May I know what did I missed ?
Thanks