Wednesday, October 19, 2016

Upsert Request using Alternative key

In MS CRM 2016, now we can insert or update request in a single request called Upsert. 
If record with particular key already exists in system, then it will be updated, if not then new record will be created. 

 More details are available  on MSDN site

https://msdn.microsoft.com/en-us/library/dn932135.aspx

Now in CRM 2016 we can create alternative keys, which are either single attribute or combination of attributes, you can use this alternative key for insert / update record. 


1. Single Attribute in alternative Key upsert

I created custom entity called Car, with attributes car no (whole number), name (string), make year (whole number) and Manufacture (string). And defined alternative key on car no. 


My Alternative Key name is - new_carNoId

When using Upsert request, we need to specify attribute name in request NOT key name. 
Here is sample code

Entity car = new Entity(new_car.EntityLogicalName, "new_carno", 2);
car["new_name"] = "Carolla";
car["new_make"] =2015;
car["new_manufacture"] = "Toyota";

UpsertRequest req = new UpsertRequest()
 {
   Target = car
 };


 UpsertResponse resp = (UpsertResponse) serviceProxy.Execute(req);


When creating entity object for insert / update, we need to specify alternate key attribute name and value, and then set other required attributes of record. 

Note: When you create alternative key, that key must be active to use Upsert, if alternative key status pending / failed, execute method will throw error. 

2. Multiple Attribute in alternative Key upsert
If you have alternative key with multiple attributes, then you need to initialize entity object differently. 

I created alternative key in my car entity with Car No (whole Number) and Manufacture (string) attributes. 

When initializing entity object at that time we need to specify KeyAttributeCollection 
and add all your key attribute in this collection. 

 KeyAttributeCollection keyColl = new KeyAttributeCollection();
     keyColl.Add("new_carno", objcar.carNo);
     keyColl.Add("new_manufacture", objcar.Manufacture);

     Entity car = new Entity(new_car.EntityLogicalName, keyColl);
     car["new_name"] = objcar.CarName;
     car["new_make"] = objcar.MakeYear;

     UpsertRequest req = new UpsertRequest()
       {
         Target = car
       };
     UpsertResponse resp = (UpsertResponse) serviceProxy.Execute(req);

When Upsert request is executed, at that time CRM will look for combination of Car No and Manufacture, if any record exists in system, then it will be updated, otherwise it record will be created. 

If you have lots of records and wants to insert / update then you can use UpsertResponse in 
ExecuteMultipleRequest  request also. 

List<CarDetails> cars = new List<CarDetails>();
cars.Add(new CarDetails(1, "X15", "BMW", 2016));
cars.Add(new CarDetails(2, "XX3", "BMW", 2014));
cars.Add(new CarDetails(3, "XX4", "BMW", 2014));
cars.Add(new CarDetails(4, "XL3", "BMW", 2014));
cars.Add(new CarDetails(1, "1Carolla CS", "Toyota", 2014));
cars.Add(new CarDetails(2, "1Carolla LE", "Toyota", 2015));
cars.Add(new CarDetails(3, "Carolla SE", "Toyota", 2015));


ExecuteMultipleRequest multipleReq = new ExecuteMultipleRequest()
 {
  Settings = new ExecuteMultipleSettings()
   {
     ContinueOnError = true,
     ReturnResponses = true
    },
   Requests = new OrganizationRequestCollection()
  };

foreach (var objcar in cars)
 {
  KeyAttributeCollection keyColl = new KeyAttributeCollection();
  keyColl.Add("new_carno", objcar.carNo);
  keyColl.Add("new_manufacture", objcar.Manufacture);

  Entity car = new Entity(new_car.EntityLogicalName, keyColl);
  car["new_name"] = objcar.CarName;
  car["new_make"] = objcar.MakeYear;

 UpsertRequest req = new UpsertRequest()
 {
   Target = car
  };

  multipleReq.Requests.Add(req);
 }


 ExecuteMultipleResponse mulresponse = (ExecuteMultipleResponse)_serviceProxy.Execute(multipleReq);


Note:  With ExecuteMultipleRequest you can execute 999 requests at a time. If you have more records than 999 in ExecuteMultipleRequest Collection, then CRM will throw error. 

Tuesday, October 18, 2016

MS CRM 2016 Web API Operations - Retrieve Single or Multiple Records

In MS CRM 2016, Web API introduced to perform operations on MS CRM using JavaScript. 
You can use web API in HTML web resources, form scripts or in ribbon commands. 
Web API uses JSON to send and receive data from CRM. 

When you are using Web API within CRM application, then we don't need to authenticate. but if using web API outside of CRM, then you need to authenticate. 

With On-premise deployments, you can use user's network credentials to authenticate CRM web API. 

For Online or internet facing deployments (IFD), you must use OAuth for Authentication. 

For more details about authenticate to MS CRM with Web API, check 
https://msdn.microsoft.com/en-us/library/mt595798.aspx


Web API Operations 

1. Retrieve Single record
     - When retrieving single record  using Web API, you must need record ID. 
     - You can specify list of attributes you want to retrieve from that entity, as  $select=accountrolecode,fullname,gendercode
     - With request you can specify this data retrieval you want synchronous or Asynchronous, as well as you can specify do you want formatted values to be retrieved or not. 

Formatted values - mainly used for option sets, entity reference, status reason, state code etc, When getting formatted values, you will get Text values also. 

e.g. StateCode =0 and Formatted State Code = Active
  
Using XMLHttpRequest


var contactId = Xrm.Page.data.entity.getId();
var req = new XMLHttpRequest();
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.1/contacts("+contactId +")?$select=accountrolecode,fullname,gendercode", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function () {
    if (this.readyState === 4) {
        req.onreadystatechange = null;
        if (this.status === 200) {
            var result = JSON.parse(this.response);
            var accountrolecode = result["accountrolecode"];
            var fullname = result["fullname"];
            var gendercode = result["gendercode"];
        }
        else {
            alert(this.statusText);
        }
    }
};
req.send();

When XMLHttpRequest open, at that time we can specify, how to retrieve data - synchronous or Asynchronous
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/contacts(B2DF0F4F-737A-E611-80DB-C4346BAC4B78)?$select=accountrolecode,fullname,gendercode"true);

If it is True, then Asynchronous request, and when false then synchronous request. 

Also if you want formatted values to be included, then you need to set request header
req.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");

and to get value of formatted attribute 
var gendercode_formatted = result["gendercode@OData.Community.Display.V1.FormattedValue"];

Using Jquery 
When using Jquery, you need to add Jquery libraries in form librarians. 


var contactId = Xrm.Page.data.entity.getId();
$.ajax({
    type: "GET",
    contentType: "application/json; charset=utf-8",
    datatype: "json",
    url: Xrm.Page.context.getClientUrl() + "/api/data/v8.1/contacts("+contactId+")?$select=accountrolecode,fullname,gendercode",
    beforeSend: function (XMLHttpRequest) {
        XMLHttpRequest.setRequestHeader("OData-MaxVersion", "4.0");
        XMLHttpRequest.setRequestHeader("OData-Version", "4.0");
        XMLHttpRequest.setRequestHeader("Accept", "application/json");
        XMLHttpRequest.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");
    },
    async: false,
    success: function (data, textStatus, xhr) {
        var result = data;
        var accountrolecode = result["accountrolecode"];
        var accountrolecode_formatted = result["accountrolecode@OData.Community.Display.V1.FormattedValue"];
        var fullname = result["fullname"];
        var gendercode = result["gendercode"];
        var gendercode_formatted = result["gendercode@OData.Community.Display.V1.FormattedValue"];
    },
    error: function (xhr, textStatus, errorThrown) {
        alert(textStatus + " " + errorThrown);
    }
});


2. Retrieve Multiple records

When retrieving multiple records, request will be more similar to single record request.
When retrieving multiple records, we need to specify filter criteria using $filter=

function retireveAllChildContacts() {
    var parentAccountId = Xrm.Page.getAttribute('parentcustomerid').getValue();
    var req = new XMLHttpRequest();
    req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/contacts?$select=accountrolecode,firstname,fullname,gendercode,lastname,middlename,_ownerid_value&$filter=_accountid_value eq " + parentAccountId + "", false);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");
    req.setRequestHeader("Prefer", "odata.maxpagesize=10");
    req.onreadystatechange = function () {
        if (this.readyState === 4) {
            req.onreadystatechange = null;
            if (this.status === 200) {
                var results = JSON.parse(this.response);
                for (var i = 0; i < results.value.length; i++) {
                    var accountrolecode = results.value[i]["accountrolecode"];
                    var accountrolecode_formatted = results.value[i]["accountrolecode@OData.Community.Display.V1.FormattedValue"];
                    var firstname = results.value[i]["firstname"];
                    var fullname = results.value[i]["fullname"];
                    var gendercode = results.value[i]["gendercode"];
                    var gendercode_formatted = results.value[i]["gendercode@OData.Community.Display.V1.FormattedValue"];
                    var lastname = results.value[i]["lastname"];
                    var middlename = results.value[i]["middlename"];
                    var _ownerid_value = results.value[i]["_ownerid_value"];
                    var _ownerid_value_formatted = results.value[i]["_ownerid_value@OData.Community.Display.V1.FormattedValue"];
                }
            }
            else {
                alert(this.statusText);
            }
        }
    };
    req.send();
}

With multiple record request if want to retrieve count also, then you can specify count in request as 
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/contacts?$select=accountrolecode&$filter=_accountid_value eq 95A043CF-0CAF-E511-80EC-0050568C6C1C&$count=true", false);

you will get count after request successful
var recordCount = results["@odata.count"];


With API you can impersonate request, You can specify user Id when sending request. Retrieval operation will be performed  based on user security permission. 
If impersonated user does not exists in CRM or not enabled, then request will through error. 

To impersonate request, you need to add request header as 
req.setRequestHeader("MSCRMCallerID", "0EB938AA-7395-E611-80DF-C4346BAD17C4");