Powered By Blogger

Thursday, December 28, 2023

Dynamics CRM - Bulk Create, Update, Delete Records in Dynamics 365

 Many times, we need to perform job in a bulk programmatically. So, instead of calling each time the request to perform the operation we can do that by using "Service.Execute()" method which uses "ExecuteMultipleRequest()" method. It adds up all those single request and creates collection and then Execute at one go. So, instead of calling everytime "Service.Create()" or "Service.Update()" or "Service.Delete()". We can do that with single "Service.Execute()" method. Below is the code that shows, how you can do all these operations:

 // Bulk Delete Operation

        public static int BulkDelete(IOrganizationService service, EntityCollection entities)

        {

            // Create an ExecuteMultipleRequest object.

            var multipleRequest = 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()

            };


            // Add a DeleteRequest for each entity to the request collection.

            foreach (var entity in entities.Entities)

            {

                EntityReference entityRef = entity.ToEntityReference(); // Assuming each entity has an EntityReference property

                if (entityRef != null)

                {

                    DeleteRequest deleteRequest = new DeleteRequest { Target = entityRef };

                    multipleRequest.Requests.Add(deleteRequest);

                }

            }


            // Execute all the requests in the request collection using a single web method call.

            ExecuteMultipleResponse multipleResponse = (ExecuteMultipleResponse)service.Execute(multipleRequest);


            return multipleResponse.Responses.Count;

        }


 // Bulk Create Operation


        public static int BulkCreate(IOrganizationService service, List<Entity> recordsToCreate)

        {

            // Create an EntityCollection populated with the list of entities.

            EntityCollection entities = new EntityCollection(recordsToCreate)

            {

                // All the records must be for the same table.

                EntityName = recordsToCreate[0].LogicalName

            };


            // Instantiate CreateMultipleRequest

            CreateMultipleRequest createMultipleRequest = new CreateMultipleRequest()

            {

                Targets = entities,

            };


            // Send the request

            CreateMultipleResponse createMultipleResponse =

                        (CreateMultipleResponse)service.Execute(createMultipleRequest);


            return createMultipleResponse.Results.Count;

        }


 // Bulk Update Operation

        public static int BulkUpdate(IOrganizationService service, EntityCollection entities)

        {

            // Create an ExecuteMultipleRequest object.

            var multipleRequest = 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()

            };


            // Add an UpdateRequest for each entity to the request collection.

            foreach (var entity in entities.Entities)

            {

                UpdateRequest updateRequest = new UpdateRequest { Target = entity };

                multipleRequest.Requests.Add(updateRequest);

            }


            // Execute all the requests in the request collection using a single web method call.

            ExecuteMultipleResponse multipleResponse = (ExecuteMultipleResponse)service.Execute(multipleRequest);


            return multipleResponse.Responses.Count;

        }

Friday, December 22, 2023

Power Apps Portal - Retrieve Data using Portal Web API

With the more recent versions of the PowerApps Portals, we now have the ability to utilize Portals Web APIs. There are some limitations and requirements around this, however, it allows us to use CRUD operations directly from JS. You can find out about this here: https://learn.microsoft.com/en-us/power-pages/configure/web-api-overview

Here, as an example, I will show you how to retrieve single record using Portal Web API. It's important to know below:

1. That your portal version must be 9.3.3.x or later for this feature to work.

2. You need to enable the site settings for the particular entity you are going to retrieve from the Dataverse.


Web API URL and versioning

Construct the Web API URL by using the format in the following table.

For example, use this format when referring a case:

https://contoso.powerappsportals.com/_api/case

All Web API resources will follow the respective table permissions in context with web roles.


HTTP methods

HTTP requests can use different kinds of methods. However, the portals Web API only supports the methods in the following table:


HTTP headers

The Web API only supports JSON. Each HTTP header must include:

An Accept header value of application/json, even when no response body is expected.
If the request includes JSON data in the request body, you must include a Content-Type header with a value of application/json.

Example:

Firstly, We need to add the "Wrapper AJAX function for the CSRF token". For this, You need to create a New Web Template:

First Web Template - WebAPIAjax

<script>
(function(webapi, $){
        function safeAjax(ajaxOptions) {
            var deferredAjax = $.Deferred();
    
            shell.getTokenDeferred().done(function (token) {
                // add headers for AJAX
                if (!ajaxOptions.headers) {
                    $.extend(ajaxOptions, {
                        headers: {
                            "__RequestVerificationToken": token
                        }
                    });
                } else {
                    ajaxOptions.headers["__RequestVerificationToken"] = token;
                }
                $.ajax(ajaxOptions)
                    .done(function(data, textStatus, jqXHR) {
                        validateLoginSession(data, textStatus, jqXHR, deferredAjax.resolve);
                    }).fail(deferredAjax.reject); //AJAX
            }).fail(function () {
                deferredAjax.rejectWith(this, arguments); // on token failure pass the token AJAX and args
            });
    
            return deferredAjax.promise();  
        }
        webapi.safeAjax = safeAjax;
    })(window.webapi = window.webapi || {}, jQuery)
</script>


Now, you need to create a second new web template to retrieve data from the Dataverse via JS that consumes the above wrapper AJAX function to call the web API.

Second Web Template - Get Data from Dataverse

{% assign title = 'My Second Web Template to Consume Web API' %}
{% include 'WebAPIAjax' %}

<div class="container">
<div class="page-heading">
{% block breadcrumbs %}
{% include 'Breadcrumbs' %}
{% endblock %}
</div>
<div class="row">
{% block title %}
<h2><b>{{title}}</b></h2>
{% endblock %}
</div>
</div>

<script>
  function getData() {
    try {
      var Id= "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";            
      var webApiUrl = "/_api/entityName(" + Id + ")?$select=attr1";
      var attr1Val = getResponse(webApiUrl).attr1;
    }
    catch (error) {
      alert("Error!" + error);
    }
  }

  function getResponse(webApiUrl) {
    var response = null;
    webapi.safeAjax({
      type: "GET",
      url: webApiUrl,
      contentType: "application/json",
      async: false,
      success: function (res) {
        response = res;
      },
    });
    return response;
  }
</script>

So, here getResponse(webApiUrl) method will connect to the Web API using the above Wrapper AJAX Function and get the data from the Dataverse. It will return the entire response in JSON format and since, we have only selected "attr1" in the query, it will return only that attribute in the response and for fetching multiple attributes, you just need to pass all those attributes in the "select" parameter in the URL (Note: Don't forget to enable, all those fields in the Web API site settings otherwise it won't be able to retrieve it.).

Likewise, you can perform the other operations as well like below:

CREATE:

webapi.safeAjax({
type: "POST",
url: "/_api/accounts",
contentType: "application/json",
data: JSON.stringify({
"name": "Sample Account"
}),
success: function (res, status, xhr) {
console.log("entityID: "+ xhr.getResponseHeader("entityid"))
}
});

UPDATE:

webapi.safeAjax({
type: "PATCH",
url: "/_api/accounts(00000000-0000-0000-0000-000000000001)",
contentType: "application/json",
data: JSON.stringify({
"name": "Sample Account - Updated"
}),
success: function (res) {
console.log(res);
}
});

DELETE:

webapi.safeAjax({
type: "DELETE",
url: "/_api/accounts(00000000-0000-0000-0000-000000000001)",
contentType: "application/json",
success: function (res) {
console.log(res);
}
});


Tools for Unit Testing in Power Platform