Basic CRUD using Xrm.WebApi

UPDATE (18/10): It appears Andrii got to this topic first -> http://butenko.pro/2017/10/05/microsoft-dynamics-365-v9-0-usage-of-new-oob-webapi-functions-part-1/. I should have probably subscribed to his RSS feed – could have saved some time for me. Anyway there is also a Part 2 that he has not posted yet, so I am looking forward to see what I missed.

Dynamics 365 Customer Engagement v9 has added CRUD functionality to query the WebAPI endpoint using Client API.

Xrm Web Api.png

Based on my initial analysis, this seems to be a work in progress and more functions will be added over time. This is some sample code how you can do the basic CRUD using this new feature. This is not an exhaustive documentation, but considering that there is nothing about this in the official documentation, it is a starting point.

Create : Method signature is ƒ (entityType, data)

Sample code to create 3 contact records

[...new Array(3).keys()].forEach(x => Xrm.WebApi.createRecord('contact', {
    firstname: 'Test',
    lastname: `Contact${x}`
}).then(c => console.log(`${x}: Contact with id ${c.id} created`))
  .fail(e => console.log(e.message)))

WebApi Create.png

Retrieve: Method signature is ƒ (entityName, entityId, options)

Sample code to retrieve contact record based on the primary key

Xrm.WebApi.retrieveRecord('contact', 'cadf8ac6-17b1-e711-a842-000d3ad11148', '$select=telephone1')
  .then(x => console.log(`Telephone: ${x.telephone1}`))
  .fail(e => console.log(e.message))

WebApi Retrieve

RetrieveMultiple: Method signature is f(entityType, options, maxPageSize)

Sample code to retrieve 10 contact records without any conditions.

Xrm.WebApi.retrieveMultipleRecords('contact', '$select=fullname,telephone1', 10)
  .then(x => x.entities.forEach(c => console.log(`Contact id: ${c.contactid}, fullname: ${c.fullname}, telephone1: ${c.telephone1}`)))
  .fail(e => console.log(e.message))

WebApi RetrieveMultiple.png

Update: Method signature is ƒ (entityName, entityId, data)

Sample code to update field on contact record

Xrm.WebApi.updateRecord('contact', 'cadf8ac6-17b1-e711-a842-000d3ad11148', {
    telephone1: '12345'
}).then(x => console.log(`Contact with id ${x.id} updated`))
  .fail(x => console.log(x.message))<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>

WebApi Update.png

Delete: Method signature is ƒ (entityName, entityId)

Xrm.WebApi.deleteRecord('contact', '88E682D8-18B1-E711-A842-000D3AD11148')
  .then(c => console.log('Contact deleted'))
  .fail(x => console.log(x.message))

WebApi Delete.png

What is not yet done/appears to be in progress

  1. Xrm.WebApi.offline not yet implemented
  2. Ability to construct custom OData requests to pass into Xrm.WebApi.execute
  3. Batching multiple requests

You can use this on your client side code on v9. It is quite basic at the moment, but you don’t need to include any external libraries. But in more advanced scenarios, you can always use Xrm WebAPI Client till these features are made available in the Client API.

Referencehttps://docs.microsoft.com/en-au/dynamics365/get-started/whats-new/customer-engagement/new-in-july-2017-update-for-developers#new-client-apis

Advertisements

Cancelling save event based on the result of async operation

When you want to cancel a save event in CRM Dynamics 365 Customer Engagement, you use “preventDefault()”  to block the save operation. This works when you block the operation based on the information that is currently on the form/page, but it does not work, if you want to block the save based on the result of an async operation.

In this contrived example, I would like to block the save of the current form, if there exists an user with “homephone” field set to 12345. The async operation is performed by “retrieveMultipleRecords” which returns a Promise.

The code below does not work

Xrm.Page.data.entity.addOnSave((e)=>{
	Xrm.WebApi.retrieveMultipleRecords('systemuser','$select=fullname,jobtitle,homephone').then(x=>{
		console.log(`DataXml OnSave: ${Xrm.Page.data.entity.getDataXml()}`);
		if(x.entities.some(x=>x.homephone == '12345')){
			e.getEventArgs().preventDefault();
			console.log('User with homephone 12345 exists. Save blocked.');
		}
	});			
});

Result

Notice the the save event completed and form’s load event fired even though preventDefault ran. The “jobtitle” field that I modified also succeeded, when I expected it to not succeed.

Async Save block does not work

In order to block the save, you’ll have to restructure the code little differently, like the one below. Block save before async operation and explicitly call save, when your criteria for save is met and use closure variable to keep track of whether to save or not.

Working code

Xrm.Page.data.entity.addOnSave((()=>{
	let isSave = false;
	return (e)=>{
		console.log(`DataXml OnSave: ${Xrm.Page.data.entity.getDataXml()}`);
		if(isSave) {
			console.log('proceed to save');
			return;
		}
		else{
			e.getEventArgs().preventDefault();
			console.log('save blocked');			
			Xrm.WebApi.retrieveMultipleRecords('systemuser','$select=fullname,jobtitle,homephone').then(x=>{
				isSave = !x.entities.some(x=>x.homephone == '12345');
				if(isSave){
					Xrm.Page.data.save();
				}
				else{
					console.log('User with homephone 12345 exists. Save blocked.');
				}
			});			
		}
	}
})());

Result

Async Save block works.png

I have tested this only in Chrome on Dynamics 365 Online v9. Hope this is useful.