Using the Bulk API to Create Records

I wanted to build a data populator for Dynamics that created a bunch of custom records and did some associations between them.

(The reasoning is so I could be sure I was always validating the same thing).

To do this, I wanted to use the Bulk API.  The Bulk API has a limit of 1000 requests per message so in addition to bulk loading my requests, I needed to ensure that multiple requests could be queued up and handled by the server.

Here is some of the sample code I wrote for this interaction.

public void BulkDispatchCreateRequests<T>(List<T> requests)
 {
 int MaxBatchSize = 999;
 bool CreateBatchRequest = true;

ExecuteMultipleRequest requestWithResults = null;

for (int i = 0; i < requests.Count(); i++)
 {
 if (CreateBatchRequest)
 {
 requestWithResults = new ExecuteMultipleRequest()
 {
 // Assign settings that define execution behavior: continue on error, return responses. 
 Settings = new ExecuteMultipleSettings()
 {
 ContinueOnError = false,
 ReturnResponses = true
 },
 // Create an empty organization request collection.
 Requests = new OrganizationRequestCollection()
 };
 }
 
 requestWithResults.Requests.Add(requests[i] as OrganizationRequest);

if (requestWithResults.Requests.Count() == MaxBatchSize || requestWithResults.Requests.Count() == requests.Count())
 {
 try
 {
 ExecuteMultipleResponse responseWithResults = (ExecuteMultipleResponse)_service.Execute(requestWithResults);
 CreateBatchRequest = true;
 System.Diagnostics.Debug.WriteLine("Bulk Accessor Request Sent.");
 }
 catch (Exception ex)
 {
 System.Diagnostics.Debug.WriteLine("Could not bulk create Accessor - error: " + ex.ToString());
 }
 }
 else
 {
 CreateBatchRequest = false;
 }
 }
 }

I used 999 as my max batch size to proactively stay under the 1,000 item limit.

The big piece of code here is that all my record Create requests are batched and associated to a ExecuteMultipleRequest. Once this is all done you bundle into the Execute request and send them off.

I got a bit fancy in this implementation and created a generic function called BulkDispatchCreateRequests which allows me drop in Associate, Delete, Disassociate requests that can all be bulk handled and loaded without having to rewrite the function.

The implementation then becomes pretty simple in other areas of my code.

BulkDispatchCreateRequests<CreateRequest>(securityRequests);

 

Dynamics SDK Paging Records

I never thought I’d have a case when I’d need to work with over 5,000 records from the Dynamics SDK.

The scenario here is that I was trying to reproduce some production issues and validate out some code changes with a higher threshold of objects.  However, as I started testing, I noticed I wasn’t bringing back everything and had hit the that magical 5,000 limit.

Without wanting to change it, I modified by query to support the retrieval of 5,000+ items that would return to my service to be worked on.

bool KeepSearching = true;
 EntityCollection results = null;
 Dictionary <Guid, Entity> Accessors = new Dictionary<Guid, Entity>();

QueryExpression query = new QueryExpression();
 query.EntityName = "contact";
 query.Criteria.AddCondition(new ConditionExpression("contactid", ConditionOperator.Equal, ContactId));
 query.ColumnSet = new ColumnSet(new string[] { "contactid", "fullname"});
 query.PageInfo = new PagingInfo();
  query.PageInfo.PageNumber = 1;

results = _service.RetrieveMultiple(query);

while (KeepSearching)
 {
 AppendContactResults(Accessors , results);

if (results.MoreRecords)
 {
 query.PageInfo.PageNumber++;
 query.PageInfo.PagingCookie = results.PagingCookie;
 results = _service.RetrieveMultiple(query);
 }
 else
 {
 KeepSearching = false;
 }
 }

The code itself is pretty simple – I append the paging info and request for the first page to come back (note even if you do not exceed the first page, you don’t need to change this code).

When the results are returned, I check if there are moreresults.  If there are no more results, I process what I have, kill the while loop and move on.  If there ARE more results, I queue up my paging information, set the watermark (the PagingCookie) and make another call to see what I missed.

The loop takes over and will close itself when there are no more results.

 

Retrieving Configuration Values from the USD

In the USD you can create Options that you can use within your Actions/Events/etc in the Unified Service Desk Configuration.

You can also access these values from within your own control via C# relatively easily.

Capture

To access the value within my USD Custom Hosted Control via C# all that is needed is the following code.

GetConfigurationValue("FoodforThought");

And now I can use this value within the USD without having to create my own configuration files et al in C#.

ASP.NET Web API Usage with Dynamics Web Api

I’ve blogged about connection setup with the Dynamics Web API over the last little while – Getting it Setup and how to use with the USD.

One critical piece that I forgot (or rather overlooked) that needs to be mentioned is what is called the “Headless” connection.  This is a connection to your Dynamics tenant that does not use a Username or Password combination and does not prompt the user to enter in their credentials when accessing.

In my previous examples, my Application type was “native”, however, for this to work in a headless modality in a web api world, this needs to be configured as Web app / API.  If not configured this way, you will always be prompted for access to your Dynamics

pluginevents.png

Once you have done this, you then need to ensure that your application exists in Dynamics to access your system.  This forum post is especially helpful in going through the steps to complete this process.

After adding my user, I added a custom security role to our solution specifically for my application that limited it’s freedom within Dynamics.

Identifying when a Dynamics Plugin Fires

I recently had a problem where we had multiple events tied to a single plugin (not the perfect architecture, but one I had to work with) on the disassociation of a record from a Many-2-Many grid.

pluginevents.png

The problem with the above implementation is that for each Disassociation even we were registered for both the Pre and Post events (whereas one plugin required both, but another only required the Post event).

To detect which even the plugin was handling I was able to key off the following Input Parameter to what event was firing.

localContext.PluginExecutionContext.InputParameters.Contains("parentExecutionId")

If the “parentExecutionId” variable existed, I was in the Pre-Step and if it did not I was in the Post-Step.

Unified Service Desk and the Dynamics Web API

A follow-up to my post on Getting Started with the Dynamics Web API – on a recent project, I was building a custom hosted control in the USD that leveraged the Dynamics Web API.

However, when I went to deploy my control, my USD instance would implode and not load whatsoever.

Turns out the issue was a result of me using a higher version of the Microsoft.IdentityModel.Clients.ActiveDirectory dll (the latest 3.19) than what is supported in the USD (using 2.22).

The resolution was to downgrade my implementation of this dll to use 2.22 et voila, my solution deployed perfectly and my USD was no longer imploding.

I should note that for the above this is all done using v2 of the USD on a Dynamics 8.2 instance.

 

 

Saving data with the Dynamics Web Api

In my previous posts on using the Dynamics Web Api, I invested a significant amount of time in figuring out the connection protocols, querying and accessing data from the results (not being an Azure, Active Directory or JSON expert).

However, when it came to saving data, the hard work was over and I was able to do with relative ease.

To save data via the Dynamics Web API, the interface is quite similar to how you would access your controls via LateBinding except that instead of the Entity object you are consuming a JObject.

JObject phoneCall = new JObject();
 phoneCall["subject"] = "Here comes my call";
 phoneCall["phonenumber"] = "888-999-7777";
 phoneCall["scheduledend"] = DateTime.Now;
 phoneCall["description"] = "Who called me.";
//The Direction Code is always an incoming call.
 phoneCall["directioncode"] = false;

string recordId = string.Empty;
 try
 {
 HttpClient httpClient = new HttpClient();
 httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
 httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
 httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
 httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
 httpClient.BaseAddress = new Uri(dyanmicsTenant + "/api/data/v8.1/");
 HttpResponseMessage response = SendAsJsonAsync(httpClient, HttpMethod.Post, "phonecalls", phoneCall).Result;

//If the response is Successfully executed then it will return the value true
 if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
 {

//Do something super cool here.

}
 }
 catch (Exception error)
 {

throw new Exception(error.Message);
 }

There is nothing crazy in that code except for the reference in our SendAsJsonAsync event that we are creating a phonecall, to our entity “phonecalls”.

You can access the full implementation of SendAsJsonAsync here on MSDN.