Group by using FetchXML
Aggregate and Grouping functions using LINQ in CRM
Use of Alias in FetchXML
Use SQL Tracing to find the SQL query generated for Dynamics CRM Platform Operations for CRM On-Premise
Update fetch query of System/User view dynamically
How to handle Special Characters in Fetch XML
Inogic’s Top 10 Most Popular Dynamics CRM Blogs of 2014
Group by using FetchXML
In Microsoft Dynamics CRM 2011 FetchXML includes grouping and aggregation features which let us to use aggregate functions like sum, count etc.
Using Group by we can calculate sum, avg, min, max, count. But, Group by clause is not supported through LINQ in CRM.
You can only specify one aggregate attribute in a query and you cannot use the distinct keyword. To create an aggregate attribute, set the keyword aggregate to true, then specify valid entity name, attribute name and alias(variable name). You must also specify the type of aggregation you want to perform.
<fetch distinct=’false’ mapping=’logical’ aggregate=’true’>
<entity name=’entity name’>
<attribute name=’attribute name’ aggregate=’count’ alias=’alias name’/>
</entity>
</fetch>”
Below is the example to get sum of total amount of all won quotes:
string quotes = @”
<fetch distinct=’false’ mapping=’logical’ aggregate=’true’>
<entity name=’quote’>
<attribute name=’totalamount’ alias=’totalamount_sum’ aggregate=’sum’/>
<attribute name=’statecode’ groupby=’true’ alias=’state’ />
<filter type=’and’>
<condition attribute=’ statecode ‘ operator=’eq’ value=’won’ />”+
“</filter> “+
“</entity> “+
“</fetch>”;
EntityCollection quotes_result = _service.RetrieveMultiple(new FetchExpression(quotes));
foreach (var q in quotes_result.Entities)
{
Decimal wonQuoteAmount = ((Money)((AliasedValue)q["totalamount_sum"]).Value).Value;
}
Aggregate and Grouping functions using LINQ in CRM
The QueryByAttribute class is a simple means to search for entities where attributes matches specified values.
When using LINQ, it returns IQueryable
IQueryable
where a.Address1_City.Contains(“a”)
Hope this article helps!
Use of Alias in FetchXML
Introduction:
I am sure by now almost anyone developing on Dynamics CRM would have had experience working with FetchXML queries, the Dynamics CRM proprietary format to query data using Dynamics CRM SDK API.
We recently came across a scenario where we needed to use an alias for an attribute. Similar to the SQL query
Select fullname as ContactName from Contact
Our search to get this work with FetchXML led us to the finding that this is indeed possible!!!
Let me first give a background on how alias has been generally used by us.
Use of Alias in Link entities:
To design queries that require you to join one or more tables, we make use of Link-Entity. The Link-Entity syntax requires providing of an Alias using which the values of the attributes can be accessed from the resultset. If you do not provide an explicit alias, the platform would auto generate the alias for you.
Here is sample fetchxml that will read the details of the invoice and the related customer be it account or contact.
string query = @”<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’false’>
<entity name=’invoice’>
<attribute name=’name’/>
<attribute name=’customerid’/>
<attribute name=’statuscode’/>
<attribute name=’totalamount’/>
<attribute name=’invoiceid’/>
<order attribute=’name’ descending=’false’/>
<link-entity name=’account’ from=’accountid’ to=’customerid’ link-type=’outer’ alias=’acc’/>
<attribute name=’address1_line1’/>
<attribute name=’address1_line2’/>
<attribute name=’address1_city’/>
<attribute name=’address1_country’/>
<attribute name=’address1_postalcode’/>
<attribute name=’address1_stateorprovince’/>
<attribute name=’emailaddress1′>
</link-entity>
<link-entity name=’contact’ from=’contactid’ to=’customerid’ link-type=’outer’ alias=’con’>
<attribute name=’address1_line1’/>
<attribute name=’address1_line2’/>
<attribute name=’address1_city’/>
<attribute name=’address1_country’/>
<attribute name=’address1_postalcode’/>
<attribute name=’address1_stateorprovince’/>
<attribute name=’emailaddress1’/>
<link-entity
</entity>
</fetch> “;
We can use following code to execute fetchxml to Retrieve the results .
//create FetchExpression object and pass fetchxml
FetchExpression fetchExpression = new FetchExpression(query);
//Execute the fetchxml to retrieve records
EntityCollection entCollection = _service.RetrieveMultiple(fetchExpression);
Now here if you would like to read attribute values of Linked entities then it can be possible using following ways.
Read Account entity attributes as follows.
((AliasedValue)entity.Attributes["acc.emailaddress1"]).Value
Read Contact entity attributes as follows.
((AliasedValue)entity.Attributes["con.emailaddress1"]).Value
Use Alias for an attribute
The examples in SDK always used Alias for aggregation and so it was my understanding that Alias for attributes can only be used in case of aggregation. But simply trying out the below query actually returned desired results.
Here we have to read entity attribute values which can be done using alias as shown below. You can specify alias for each attribute and then read values by referring the Alias provided.
<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’false’>
<entity name=’invoice’>
<attribute name=’name’ alias=’inv_name’/>
<attribute name=’totalamount’alias=’inv_amount’/>
<attribute name=’customerid’alias=’inv_customer’/>
<order attribute=’name’ descending=’false’/>
</entity>
</fetch>
You would now read the attribute values as shown below with the Alias name instead of the attribute name.
((EntityReference)((AliasedValue)entity.Attributes["inv_customer"]).Value).Name
Use of Alias for Attribute in Link Entity Queries
To take this one step further, we decided to test by margining both Link Entity alias and also providing an alias to the attribute read from the Link-Entity to check how that works.
Here following fetch query will show how to give alias names to the link entity attributes. By using this alias names we are able to retrieve the link entities attributes as well.
<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’false’>
<entity name=’invoice’>
<attribute name=’name’/>
<attribute name=’customerid’/>
<attribute name=’statuscode’/>
<attribute name=’totalamount’/>
<attribute name=’invoiceid’/>
<order attribute=’name’ descending=’false’/>
<link-entity name=’account’ from=’accountid’ to=’customerid’ link-type=’outer’ alias=’acc’>
<attribute name=’address1_line1′ alias = ‘Line1’/>
<attribute name=’address1_line2′ alias = ‘Line2’/>
<attribute name=’address1_city’ alias = ‘city’/>
<attribute name=’address1_country’ alias = ‘country’/>
<attribute name=’address1_postalcode’ alias = ‘code’/>
<attribute name=’address1_stateorprovince’ alias = ‘state’/>
<attribute name=’emailaddress1′ alias = ‘email’/>
</link-entity>
<link-entity name=’contact’ from=’contactid’ to=’customerid’ link-type=’outer’ alias=’con’>
<attribute name=’address1_line1′ alias = ‘cont_Line1’/>
<attribute name=’address1_line2′ alias = ‘cont_Line2’/>
<attribute name=’address1_city’ />
<attribute name=’address1_country’ alias = ‘cont_country’/>
<attribute name=’address1_postalcode’ alias = ‘cont_code’/>
<attribute name=’address1_stateorprovince’ alias = ‘cont_state’/>
<attribute name=’emailaddress1′ alias = ‘cont_email’/>
</link-entity>
</entity>
</fetch>
In the first query for link entity provided above, we needed to prefix the attribute with the entity alias to read the values. But here we can directly read the values as follows
((AliasedValue)entity.Attributes["cont_city"]).Value
if the Alias had not been provided at the attribute level the value would be read as
((AliasedValue)entity.Attributes["con. address1_city"]).Value
Conclusion:
Alias can be used for the Entities in case of Link-Entity as well as for Attributes even if there is no aggregation provided.
Use SQL Tracing to find the SQL query generated for Dynamics CRM Platform Operations for CRM On-Premise
Dynamics CRM works largely on FetchXML queries. FetchXML is a proprietary query language for Dynamics CRM. For developers coming from SQL background, they would really want to find the SQL query that the FetchXML translates into. While one way is to explain the FetchXML syntax, it might help to also get the SQL statement that the CRM Platform converts into.
SQL Tracing is what comes to the rescue here. Using SQL Tracing, you can find all the SQL queries that get executed in the background when running CRM application.
Note: SQL Tracing can only be executed for CRM On-Premise systems.
Take an example where we have a below Fetch XML based query that is generated using advance find.
<fetch version=”1.0″ output-format=”xml-platform” mapping=”logical” distinct=”false”>
<entity name=”account”>
<attribute name=”name” />
<attribute name=”primarycontactid” />
<attribute name=”telephone1″ />
<attribute name=”accountid” />
<order attribute=”name” descending=”false” />
<filter type=”and”>
<condition attribute=”createdon” operator=”last-x-years” value=”4″ />
</filter>
</entity>
</fetch>
Follow the below steps to get the SQL Query for the above.
- Open SQL Server Profiler and create a new trace.
To create a new trace click on File -> New Trace.
- Then start tracing by clicking on Run button.
- Open CRM environment and create an advance find for above query and click on Results button to execute that query.
- Then stop tracing and find the appropriate trace entry where you can see the SQL Query that was executed by CRM Platform in the background when we execute query from advance find.
In this way you will get the SQL query from the fetch XML.
exec sp_executesql N’select
top 51 “account0″.Name as “name”
, “account0″.PrimaryContactId as “primarycontactid”
, “account0″.Telephone1 as “telephone1″
, “account0″.AccountId as “accountid”
, “account0″.PrimaryContactIdName as “primarycontactidname”
, “account0″.PrimaryContactIdYomiName as “primarycontactidyominame”
from
Account as “account0″ (NOLOCK)
where
(( “account0″.CreatedOn >= @CreatedOn0 and “account0″.CreatedOn <= @CreatedOn1 )) order by
“account0″.Name asc
, “account0″.AccountId asc’,N’@CreatedOn0 datetime,@CreatedOn1 datetime’,@CreatedOn0=’2010-05-03 07:00:00′,@CreatedOn1=’2014-05-03 11:52:49.553′
You can see from the SQL query, there is hardcoded date for condition i.e. Created on date on Last X Year.
There are built in SQL functions to query CRM data. In this case you can use “dbo.fn_LastXYear(<date>,<number of year>)”.
Conclusion:
Use this to understand the SQL query behind any CRM operation performed from the UI.
To generate FetchXML for a SQL query you have in hand you can use the online tool available at http://www.sql2fetchxml.com/ from KingswaySoft
Update fetch query of System/User view dynamically
Based on the client requirement sometimes it is needed to update the system/user view to filter record as per needed. In your query you need to apply outer join which won’t be possible through Advanced Find so in that case you need to update the fetch query of System/User view dynamically. Below is the sample code how to update the fetch query of system/user view dynamically using link type as Outer Join. Below is the query which describes that show only those active contacts with no opportunity exist in contact and those opportunities which was not closed in last 9 months using Outer Join. Below query won’t be possible through advanced find.
string fetchXml = @”<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’true’ >
<entity name=’contact’ >
<attribute name=’fullname’ />
<attribute name=’parentcustomerid’ />
<attribute name=’emailaddress1′ />
<attribute name=’telephone3′ />
<attribute name=’address1_city’ />
<link-entity name=’opportunity’ from=’customerid’ to=’contactid’ alias=’ab’ link-type=’outer’ >
<filter type=’or’ >
<condition attribute=’actualclosedate’ operator=’last-x-months’ value=’9′ />
</filter>
</link-entity>
<filter type=’and’ >
<condition attribute=’statecode’ operator=’eq’ value=’0′ />
<condition entityname=’ab’ attribute=’opportunityid’ operator=’null’ />
</filter>
</entity>
</fetch>”;
Entity view = newEntity(“savedquery”); OR Entity view = newEntity(“userquery”);
view["fetchxml"] = fetchXml;
view.Id = newGuid(“8DF19B44-A073-40C3-9D6D-EE1355D8C4BA”);
service.Update(view);
You can get the GUID of view from the URL of view. Refer the below screenshot:
Note: The attributes you define in a fetch query to show in the view should match with the columns that have been added in the view manually. Refer the below screenshot:
How to handle Special Characters in Fetch XML
Fetch XML, the easiest way to write complex queries to retrieve data by joining multiple entities.
Though being the easiest, we were still stuck at a point where we didn’t know what to do. We were getting Invalid XML error.
For quite some time we were wondering what could have caused this. Below is our Fetch XML.
What is this XML for? Through this XML, we are trying to find the Forecast record with the specified Forecast Name.
Code:
string forecastName = “City & Lights (Sample) December 2014″;
string xml = @”<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ encoding=’Windows-1252′ distinct=’false’>” +
“<entity name=’new_forecast’>” +
“<attribute name=’new_forecastid’ />” +
“<attribute name=’new_name’ />” +
“<order attribute=’new_name’ descending=’false’ />” +
“<filter type=’and’>” +
“<condition attribute=’new_name’ operator=’eq’ value='” forecastName + “‘ />” +
“</filter>” +
“</entity>” +
“</fetch>”;
Things were pretty good till the time we didn’t have any special character in the forecast name (For this demo we have hard coded the Forecast Name, in reality it is dynamically created). Then came the sore part, we got a Forecast Name with a special character “&” in it. It took us around 45 minutes to figure out the reason of getting Invalid XML error.
The reason for the Invalid XML error was the special character “&”. We need to encode the special characters before using it in a Fetch XML.
Then, we tried encoding the forecastName. We used the below assembly & code to encode the forecastName.
Assembly Used: System.Web
Code Used: HttpUtility.HtmlEncode(forecastName)
Now, we got the below error:
While investigating this issue, we found that System.Web Assembly is not supported for Plug-In registered in Sandbox mode. And, HttpUtility.HtmlEncode(string) needs System.Web Assembly.
Now, what? We are again stuck with special characters in Fetch XML, giving us Invalid XML.
Hush! nothing to worry, we have WebUtility as our savior.
What all things are we suppose to do now?
Well, we just have to use a namespace and that`s it we are good to go.
Namespace: System.Net
Code Used: WebUtility.HtmlEncode(forecastName)
Now, the final XML look like this,
string xml = @”<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ encoding=’Windows-1252′ distinct=’false’>” +
“<entity name=’new_forecast’>” +
“<attribute name=’new_forecastid’ />” +
“<attribute name=’new_name’ />” +
“<order attribute=’new_name’ descending=’false’ />” +
“<filter type=’and’>” +
“<condition attribute=’new_name’ operator=’eq’ value='” + WebUtility.HtmlEncode(forecastName) + “‘ />” +
“</filter>” +
“</entity>” +
“</fetch>”;
The special characters in Fetch XML being the sore point once, now is very easy to tackle.
Inogic’s Top 10 Most Popular Dynamics CRM Blogs of 2014
This blog in the beginning of New Year brings you the Top 10 interesting and useful blogs posted by us during the year 2014. This article consists of content from blogs that belong to CRM 2013 and which have been the most visited, re-tweeted and commented blogs of Inogic. Hope these set of blogs are resourceful and help you some way or the other.
This blog talks about data security with respect to Dynamics CRM. As we are storing all our data in CRM, we need to keep our sensitive data in Encrypted format for the security reasons. Microsoft Dynamics CRM 2013 uses standard Microsoft SQL Server cell level encryption for a set of default entity attributes that contain sensitive information, such as user names and email passwords.
How to change the Port of ADFS 3.0 (Windows server 2012 R2) to 444
While configuring IFD with the setup being ADFS and CRM installed in same server i.e. Windows server 2012 R2, ADFS 3.0 on Windows server 2012 R2 does not depend on IIS. So in that case, as ADFS port cannot be changed we used to change CRM (https) port to 444. As a result of which the users need to browse CRM IFD URL as https://orgname.domainame.com:444. Hence this blog talks about how we can still achieve this in ADFS 3.0 and there would not be any more need to append the port in IFD URL which browsing CRM.
Using Ctrl + S to Save the HTML Webresource Content
Everyone might have wished that it would any day be possible to achieve the same behavior of Ctrl + S that we get on CRM pages, on the HTML web resource that we use in CRM. So that data entered in custom grids can be directly saved through form save. This blog has the solution to achieve this.
Unable to Configure Dynamics CRM Outlook Client for Org Configured for IFD Access
This blog brings you various solutions for the issues that come up after configuring IFD such as issues while connecting outlook client or issues while connecting plug-in registration tool.
How Security Role Privileges are Inherited from Team to User
Usually security privileges inherited from team to user are judged differently and so this blog explains the intricacy in more detail with examples so the team to user inheritance of privileges is clearly unfolded.
A lot of times when we develop plug-ins and workflows, we are in need of adding multiple assemblies (.dll) into our main solution. This blog illustrates the method and commands that can be used to merge needed assemblies in one and use in our solution.
How to handle Special Characters in Fetch XML
This is a rare situation where a special character was noticed while designing fetch xml query and we got stuck at that point, so this blog talks about the solution that we had used and can be very helpful.
Using Indicators in SSRS Reports
This article is also based on a very interesting topic which can be used in SSRS reports to enhance the quality and overview of the reports and i.e. “Indicators”.
Indicators are the icons that can be used to provide special effects like directions, symbols, ratings etc. on the reports.
Troubleshooting Certificate Error while configuring Claims-Based Authentication
When we configure Claims-Based Authentication in Microsoft Dynamics CRM 2011 Deployment Manager, we may receive the following error. “The encryption certificate………does not exist in the local computer certificate store”
So this blog has the solution for this error.
How to show Filtered Lookup Dialog in Dynamics CRM through Script
This blog describes a solution that we had come up for this requirement wherein we were supposed to show a Filtered Custom Lookup on form load. By Filtered Custom Lookup, it meant that we were supposed to show a Lookup dialog similar to CRM Filtered lookup (Filtered Lookup value changes on the basis of other Lookup on which it is dependent) dialog. The lookup has to be filtered on the basis of a value in the lookup field which was there on the form.
So there you go! The best of the best from the Inogic Team. Hope you find these articles useful and we would make sure we keep posting such informative articles to add to the community.
Use WEB API Batch Request in Dynamics 365 to execute long FetchXML
Introduction:
Recently we had a project where we use WEB API for retrieve the records from Dynamics CRM. In our project, we dynamically create FetchXML to retrieve records but sometimes Fetchxml have a lot of columns and conditions etc so its length get increase and we this the fetchxml in URL which violates the browser URL length rule. So When we try to execute this long length FetchXML and retrieve the records using the GET method, We were getting the error message of “HTTP Error 404 – Not Found” because of WEB API request fails to execute too long FetchXML. When we work on this error message, we find that we need to use WEB API Batch Request to execute the long FetchXML.
In the below example, we execute the long FetchXML of account entity and use POST request to execute long FetchXML.
First, we created the simple request body as below:
//create request body var bodyFetch=''; bodyFetch = '--batch_recordfetch\n' bodyFetch += 'Content-Type: application/http\n' bodyFetch += 'Content-Transfer-Encoding: binary\n' bodyFetch += '\n' bodyFetch += 'GET [CRM URL]/api/data/v8.2/accounts?fetchXml=' + fetch + ' HTTP/1.1\n' bodyFetch += 'Content-Type: application/json\n' bodyFetch += 'OData-Version: 4.0\n' bodyFetch += 'OData-MaxVersion: 4.0\n' bodyFetch += 'Prefer: odata.include-annotations=*\n' bodyFetch += '\n' bodyFetch += '--batch_recordfetch--'
Note: ‘fetch’ is the parameter of the long FetchXML.
And we pass the created the simple request body as data to Ajax request and we will receive the response of request as success or failure as below:
//create AJAX request $.ajax({ type: "POST", contentType: "application/json; charset=utf-8", datatype: "json", async: true, url: '[CRM URL]/api/data/v8.2/' + '$batch', data: bodyFetch, beforeSend: function (xhr) { //Specifying this header ensures that the results will be returned as JSON. xhr.setRequestHeader("Accept", "application/json"); xhr.setRequestHeader("OData-MaxVersion", "4.0"); xhr.setRequestHeader("OData-Version", "4.0"); xhr.setRequestHeader("Prefer", "odata.include-annotations=*"); xhr.setRequestHeader("Content-Type", "multipart/mixed;boundary=batch_recordfetch"); }, //success callback success: function (data, textStatus, xhr) { data = JSON.parse(data.substring(data.indexOf('{'), data.lastIndexOf('}') + 1)); if (data != null) { if (data.value == null) { alert("success"); } } }, //error callback error: function (xhr, textStatus, errorThrown) { alert("failed"); } });
Conclusion:
User can easily execute the long length FetchXML using the WEB API Batch Request.
To read more about FetchXML visit here.
Execute multiple FetchXML and OData queries using Batch request
We have developed an application to fulfill one of our clients’ requirement. In that application we have perform multiple retrieval operation from CRM (around 10) either using FetchXML or OData of different entities. So, when we retrieve records from Dynamics 365 CRM of different entities for each retrieve operation it takes around 300 milliseconds and thus our application takes time to load and users have to wait for application to load. For this, our clients reported that the application is slow and taking time to load and operate, so ultimately it takes time in displaying result to the users. As it is, quick turnaround time is essential for a business. Therefore, to improve the performance, we worked on it and found that most of the time it was taking was to retrieve records from Dynamics CRM as we perform multiple retrieval operation. To reduce this retrieval time, we have used Batch request and retrieved multiple records using single request instead of multiple requests thereby reducing significant amount of time.
Below is the JavaScript code used for same.
We have created this function to execute four retrieval requests in single instance to retrieve records from Dynamics CRM instead of four different requests.
//This function is used to read batch records from CRM
var readRecordsUsingBatchRequest = function (entityType, webresourceName) {
var functionName = “readRecordsUsingBatchRequest “;
var batchReq = [];
var team = “Sales”;
var userId = “”;
var languagecode = “1033”;
try {
//get the userid
userId = getUserId().replace(“{“, “”).replace(“}”, “”);
//Read system user
fetchXml = “<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’true’>” +
“<entity name=’systemuser’>” +
“<attribute name=’fullname’ />” +
“<attribute name=’businessunitid’ />” +
“<attribute name=’systemuserid’ />” +
“<link-entity name=’teammembership’ from=’systemuserid’ to=’systemuserid’ visible=’false’ intersect=’true’>” +
“<link-entity name=’team’ from=’teamid’ to=’teamid’ alias=’team’>” +
“<attribute name=’name’ />” +
“<attribute name=’teamid’ />” +
“<filter type=’and’>” +
“<condition attribute=’name’ operator=’eq’ value='” + team.toLowerCase() + “‘ />” +
“</filter>” +
“</link-entity>” +
“</link-entity>” +
“</entity>” +
“</fetch>”;
var req = {};
req.Query = fetchXml;
req.IsFetchxml = true;
req.EntitysetName = “systemusers”;
batchReq.push(req);
//Read Language webresource
var req = {};
webresourceName = “new_LanguageLables_” + languagecode + “.xml”;
req.Query = “?$select=webresourceid,content&$filter=name eq ‘” + webresourceName + “‘ “;
req.EntitysetName = “webresourceset”;
batchReq.push(req);
//Read usage (custom entity) records
fetchxml = “<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’false’>” +
“<entity name=’new_usage’>” +
“<attribute name=’new_usageid’ />” +
“<attribute name=’new_currentcount’ />” +
“<attribute name=’new_allowedlimit’ />” +
“<filter type=’and’>” +
“<condition attribute=’statecode’ operator=’eq’ value=’0′ />” +
“</filter>” +
“</entity>” +
“</fetch>”;
var req = {};
req.Query = fetchxml;
req.IsFetchxml = true;
req.EntitysetName = “new_usages”;
batchReq.push(req);
//Read System views
var req = {};
req.Query = “?$select=name,fetchxml,savedqueryid,layoutxml&$filter=querytype eq 0 and isquickfindquery eq false and statecode eq 0 and returnedtypecode eq ‘” + entityType + “‘”;
req.EntitysetName = “savedqueries”;
batchReq.push(req);
//retrieve using batch request
WebApiLib.batchRequestRetrieve(batchReq, function (results) {
readRecordsUsingBatchRequestCallback(result)
}, readRecordsUsingBatchRequestErrorCallBack);
} catch (e) {
showError(functionName + ” ” + e.message);
}
}
var readRecordsUsingBatchRequestCallback = function (error) {
var functionName = “readRecordsUsingBatchRequestCallback “;
try {
//Perform the required logic
} catch (e) {
showError(functionName + ” ” + e.message);
}
}
var readRecordsUsingBatchRequestErrorCallBack = function (error) {
var functionName = “readRecordsUsingBatchRequestCallback “;
try {
showError(functionName + ” ” + e.message);
} catch (e) {
}
}
In WebApi library we have written below function to execute all the above queries to retrieve records using Ajax request. When we execute the request then it gives results in following format.
Now, our next challenge is to parse above response and get the results in JSON format.
To parse the above response first we split this result using “batch” word and slice it as shown in below.
data.split(“–batch”).slice(1, reqCount + 1);
Once we split, we get the array then looping through that array get the result of individual using
result = value.substring(value.indexOf(‘{‘), value.lastIndexOf(‘}’) + 1);
And finally parse that result to get it JSON format using following line of code.
JSON.parse(result)
//Retrieval function in web api library
batchRequestRetrieve: function (requests, batchRequestRetrieveSuccess, batchRequestRetrieveError) {
var reqColl = [];
var reqCount = 0;
try {
reqCount = requests.length;
//encode the fetchxml
$.each(requests, function (index, req) {
reqColl.push(‘–batch_fetch’);
reqColl.push(‘Content-Type: application/http’);
reqColl.push(‘Content-Transfer-Encoding: binary’);
reqColl.push(”);
if (req.IsFetchxml != undefined && req.IsFetchxml == true) {
reqColl.push(‘GET ‘ + getWebAPIPath() + req.EntitysetName + ‘?fetchXml=’ + req.Query + ‘ HTTP/1.1’);
}
else if (req.FunctionName != undefined) {
reqColl.push(‘GET ‘ + getWebAPIPath() + req.FunctionName + ‘ HTTP/1.1’);
}
else {
reqColl.push(‘GET ‘ + getWebAPIPath() + req.EntitysetName + req.Query + ‘ HTTP/1.1’);
}
reqColl.push(‘Content-Type: application/json’);
reqColl.push(‘OData-Version: 4.0’);
reqColl.push(‘OData-MaxVersion: 4.0’);
reqColl.push(‘Prefer: odata.include-annotations=*’);
reqColl.push(”);
reqColl.push(‘{}’);
});
reqColl.push(‘–batch_fetch–‘);
var mainReq = reqColl.join(‘\r\n’);
//create AJAX request
$.ajax({
type: “POST”,
contentType: “application/json; charset=utf-8”,
datatype: “json”,
async: true,
url: this.getWebAPIPath() + “$batch”,
data: mainReq,
beforeSend: function (xhr) {
//Specifying this header ensures that the results will be returned as JSON.
xhr.setRequestHeader(“Accept”, “application/json”);
xhr.setRequestHeader(“OData-MaxVersion”, “4.0”);
xhr.setRequestHeader(“OData-Version”, “4.0”);
xhr.setRequestHeader(“Prefer”, “odata.include-annotations=*”);
xhr.setRequestHeader(“Content-Type”, “multipart/mixed;boundary=batch_fetch”);
},
success: function (data, textStatus, xhr) {
var results = data.split(“–batch”).slice(1, reqCount + 1);
var recCollection = [];
$.each(results, function (index, data) {
var result = getFormattedData(data);
if (JSON.parse(result).value != undefined) {
recCollection.push(JSON.parse(result).value);
}
else {
recCollection.push(JSON.parse(result));
}
});
batchRequestRetrieveSuccess(recCollection);
},
error: function (xhr, textStatus, errorThrown) {
batchRequestRetrieveError(xhr);
}
});
} catch (e) {
throw new Error(e);
}
}
getWebAPIPath: function () {
return this.getClientUrl() + “/api/data/v9.1/”;
}
getFormattedData: function (value) {
return value.substring(value.indexOf(‘{‘), value.lastIndexOf(‘}’) + 1);
}
Conclusion
With the help of Batch request we can execute multiple Fetch and OData queries to get the results. This increases the performance and reduces the retrieval time.
Field Comparisons now available for Queries in Power Platform
Dynamics 365 CRM and the platform underneath has always provided for a proprietary query language called FetchXML. With the introduction of CRM Online, this became the defacto query language as SQL commands could no longer be used (well querying data through sql is being reintroduced, under preview right now, but that is another topic for discussion)
The FetchXML syntax is not as extensive and flexible as SQL syntax and had its limitations. One of them being when you provide filter conditions, the value on the right hand side of the condition always had to be a constant static value
i.e address1_city = ‘Mumbai’
<condition attribute=’address1_city’ operator=’eq’ value=’Mumbai’ />
If we wanted to list out contacts that do not have the same city in the bill-to and ship-to addresses, we were looking for a query in the form of
Select * from contact
where contact.address1_city <> contact.address2_city
This had not been possible until now as it was comparison between values in fields rather than constant or static values.
With the new enhancements to FetchXML, it is now possible to frame this query
<fetch version=”1.0″ output-format=”xml-platform” mapping=”logical” distinct=”false” >
<entity name=”contact” >
<attribute name=”fullname” />
<attribute name=”address1_city” />
<attribute name=”contactid” />
<filter type=”and” >
<condition attribute=” address1_city” operator=”eq” valueof=”address2_city” />
</filter>
</entity>
</fetch>
If you notice to use comparison between fields, we are using valueof instead of value that we have traditionally used when comparing with static values
The field comparison supports the following operators
- Equal (eq)
- NotEqual (neq)
- GreaterThan (gt)
- GreaterEqual (ge)
- LessThan (lt)
- LessEqual (le)
<condition attribute=”FirstAttributeLogicalName” operator=”OperatorKeyword” valueof=”SecondAttributeLogicalName” />
OperatorKeyword will have one among values from the list – eq,neq,gt,ge,lt,le
Here is a table of the field types supported by each operator
Apart from FetchXML, if you would like to use the query expression, you could use the following code for field comparison.
var query = new QueryExpression(“nc_devices”);
query.ColumnSet.AddColumns(“nc_faultycount”, “nc_propercount”);
query.Criteria.Conditions.Add(new ConditionExpression(“nc_faultycount”, ConditionOperator.GreaterThan, true, “nc_propercount”));
EntityCollection results = new EntityCollection();
results=service.RetrieveMultiple(query);
Note: This field comparison feature in Query Expression can be used only for sdk assemblies with version greater than or equal to 9.0.2.25. But field comparison feature in FetchXML is available for all v9 version of sdk assemblies and also v8 versions too.
Currently these platform enhancements are available to developers for use through SDK calls. The query designer options available through the UI like the Advanced Find Query builder is yet to support this feature.
This first iteration of this feature does come with certain limitations. Please check the below link regarding the limitations.
How to solve “When using arithmetic values in Fetch a ProxyTypesAssembly must be used in order to know which types to cast values to” while using Fake XRM Easy
Introduction
While using Fake XRM Easy for Plug-in or Workflow unit testing, there could be situations that could arise where we need to use fetchXML to retrieve some records. While it works for most of the time, it can be tricky while working with some attributes statecode for starters.
In the above fetchXML, “<condition attribute=”statecode” operator=”eq” value=”0″ />” will throw the error ‘When using arithmetic values in Fetch a ProxyTypesAssembly must be used in order to know which types to cast values to.’ as shown in the below screenshot:
To overcome this, we need to generate Early Bound. For that, we will be using the Early Bound Generator plugin of XrmToolBox.
Note: Once you open up the Early Bound Generator, you need to click on Create All button to generate class files for Entities, Actions, and Optionsets. Otherwise, based on your requirement you can decide which one you need to generate.
After the process is complete, put those three files (Actions.cs, Entities.cs, Optionsets.cs) in the project you are working and change the namespace of these files to align with your project’s. For a cleaner folder structure, add a folder named Helper or of the same tone in your project and put these files in it.
After you’ve declared the context, you need to add proxy type assembly to your faked context, as shown in the screenshot below:
Note: For feasibility, use the line of code given below.
context.ProxyTypesAssembly = Assembly.GetExecutingAssembly();
Conclusion
Following the above approach, you will be able to use FetchXML to retrieve data without getting ‘When using arithmetic values in Fetch a ProxyTypesAssembly must be used in order to know which types to cast values to.’ error.
Use WEB API Batch Request in Dynamics 365 to execute long FetchXML
Introduction:
Recently we had a project where we use WEB API for retrieve the records from Dynamics CRM. In our project, we dynamically create FetchXML to retrieve records but sometimes Fetchxml have a lot of columns and conditions etc so its length get increase and we this the fetchxml in URL which violates the browser URL length rule. So When we try to execute this long length FetchXML and retrieve the records using the GET method, We were getting the error message of “HTTP Error 404 – Not Found” because of WEB API request fails to execute too long FetchXML. When we work on this error message, we find that we need to use WEB API Batch Request to execute the long FetchXML.
In the below example, we execute the long FetchXML of account entity and use POST request to execute long FetchXML.
First, we created the simple request body as below:
//create request body var bodyFetch=''; bodyFetch = '--batch_recordfetch\n' bodyFetch += 'Content-Type: application/http\n' bodyFetch += 'Content-Transfer-Encoding: binary\n' bodyFetch += '\n' bodyFetch += 'GET [CRM URL]/api/data/v8.2/accounts?fetchXml=' + fetch + ' HTTP/1.1\n' bodyFetch += 'Content-Type: application/json\n' bodyFetch += 'OData-Version: 4.0\n' bodyFetch += 'OData-MaxVersion: 4.0\n' bodyFetch += 'Prefer: odata.include-annotations=*\n' bodyFetch += '\n' bodyFetch += '--batch_recordfetch--'
Note: ‘fetch’ is the parameter of the long FetchXML.
And we pass the created the simple request body as data to Ajax request and we will receive the response of request as success or failure as below:
//create AJAX request $.ajax({ type: "POST", contentType: "application/json; charset=utf-8", datatype: "json", async: true, url: '[CRM URL]/api/data/v8.2/' + '$batch', data: bodyFetch, beforeSend: function (xhr) { //Specifying this header ensures that the results will be returned as JSON. xhr.setRequestHeader("Accept", "application/json"); xhr.setRequestHeader("OData-MaxVersion", "4.0"); xhr.setRequestHeader("OData-Version", "4.0"); xhr.setRequestHeader("Prefer", "odata.include-annotations=*"); xhr.setRequestHeader("Content-Type", "multipart/mixed;boundary=batch_recordfetch"); }, //success callback success: function (data, textStatus, xhr) { data = JSON.parse(data.substring(data.indexOf('{'), data.lastIndexOf('}') + 1)); if (data != null) { if (data.value == null) { alert("success"); } } }, //error callback error: function (xhr, textStatus, errorThrown) { alert("failed"); } });
Conclusion:
User can easily execute the long length FetchXML using the WEB API Batch Request.
To read more about FetchXML visit here.
Execute multiple FetchXML and OData queries using Batch request
We have developed an application to fulfill one of our clients’ requirement. In that application we have perform multiple retrieval operation from CRM (around 10) either using FetchXML or OData of different entities. So, when we retrieve records from Dynamics 365 CRM of different entities for each retrieve operation it takes around 300 milliseconds and thus our application takes time to load and users have to wait for application to load. For this, our clients reported that the application is slow and taking time to load and operate, so ultimately it takes time in displaying result to the users. As it is, quick turnaround time is essential for a business. Therefore, to improve the performance, we worked on it and found that most of the time it was taking was to retrieve records from Dynamics CRM as we perform multiple retrieval operation. To reduce this retrieval time, we have used Batch request and retrieved multiple records using single request instead of multiple requests thereby reducing significant amount of time.
Below is the JavaScript code used for same.
We have created this function to execute four retrieval requests in single instance to retrieve records from Dynamics CRM instead of four different requests.
//This function is used to read batch records from CRM
var readRecordsUsingBatchRequest = function (entityType, webresourceName) {
var functionName = “readRecordsUsingBatchRequest “;
var batchReq = [];
var team = “Sales”;
var userId = “”;
var languagecode = “1033”;
try {
//get the userid
userId = getUserId().replace(“{“, “”).replace(“}”, “”);
//Read system user
fetchXml = “<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’true’>” +
“<entity name=’systemuser’>” +
“<attribute name=’fullname’ />” +
“<attribute name=’businessunitid’ />” +
“<attribute name=’systemuserid’ />” +
“<link-entity name=’teammembership’ from=’systemuserid’ to=’systemuserid’ visible=’false’ intersect=’true’>” +
“<link-entity name=’team’ from=’teamid’ to=’teamid’ alias=’team’>” +
“<attribute name=’name’ />” +
“<attribute name=’teamid’ />” +
“<filter type=’and’>” +
“<condition attribute=’name’ operator=’eq’ value='” + team.toLowerCase() + “‘ />” +
“</filter>” +
“</link-entity>” +
“</link-entity>” +
“</entity>” +
“</fetch>”;
var req = {};
req.Query = fetchXml;
req.IsFetchxml = true;
req.EntitysetName = “systemusers”;
batchReq.push(req);
//Read Language webresource
var req = {};
webresourceName = “new_LanguageLables_” + languagecode + “.xml”;
req.Query = “?$select=webresourceid,content&$filter=name eq ‘” + webresourceName + “‘ “;
req.EntitysetName = “webresourceset”;
batchReq.push(req);
//Read usage (custom entity) records
fetchxml = “<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’false’>” +
“<entity name=’new_usage’>” +
“<attribute name=’new_usageid’ />” +
“<attribute name=’new_currentcount’ />” +
“<attribute name=’new_allowedlimit’ />” +
“<filter type=’and’>” +
“<condition attribute=’statecode’ operator=’eq’ value=’0′ />” +
“</filter>” +
“</entity>” +
“</fetch>”;
var req = {};
req.Query = fetchxml;
req.IsFetchxml = true;
req.EntitysetName = “new_usages”;
batchReq.push(req);
//Read System views
var req = {};
req.Query = “?$select=name,fetchxml,savedqueryid,layoutxml&$filter=querytype eq 0 and isquickfindquery eq false and statecode eq 0 and returnedtypecode eq ‘” + entityType + “‘”;
req.EntitysetName = “savedqueries”;
batchReq.push(req);
//retrieve using batch request
WebApiLib.batchRequestRetrieve(batchReq, function (results) {
readRecordsUsingBatchRequestCallback(result)
}, readRecordsUsingBatchRequestErrorCallBack);
} catch (e) {
showError(functionName + ” ” + e.message);
}
}
var readRecordsUsingBatchRequestCallback = function (error) {
var functionName = “readRecordsUsingBatchRequestCallback “;
try {
//Perform the required logic
} catch (e) {
showError(functionName + ” ” + e.message);
}
}
var readRecordsUsingBatchRequestErrorCallBack = function (error) {
var functionName = “readRecordsUsingBatchRequestCallback “;
try {
showError(functionName + ” ” + e.message);
} catch (e) {
}
}
In WebApi library we have written below function to execute all the above queries to retrieve records using Ajax request. When we execute the request then it gives results in following format.
Now, our next challenge is to parse above response and get the results in JSON format.
To parse the above response first we split this result using “batch” word and slice it as shown in below.
data.split(“–batch”).slice(1, reqCount + 1);
Once we split, we get the array then looping through that array get the result of individual using
result = value.substring(value.indexOf(‘{‘), value.lastIndexOf(‘}’) + 1);
And finally parse that result to get it JSON format using following line of code.
JSON.parse(result)
//Retrieval function in web api library
batchRequestRetrieve: function (requests, batchRequestRetrieveSuccess, batchRequestRetrieveError) {
var reqColl = [];
var reqCount = 0;
try {
reqCount = requests.length;
//encode the fetchxml
$.each(requests, function (index, req) {
reqColl.push(‘–batch_fetch’);
reqColl.push(‘Content-Type: application/http’);
reqColl.push(‘Content-Transfer-Encoding: binary’);
reqColl.push(”);
if (req.IsFetchxml != undefined && req.IsFetchxml == true) {
reqColl.push(‘GET ‘ + getWebAPIPath() + req.EntitysetName + ‘?fetchXml=’ + req.Query + ‘ HTTP/1.1’);
}
else if (req.FunctionName != undefined) {
reqColl.push(‘GET ‘ + getWebAPIPath() + req.FunctionName + ‘ HTTP/1.1’);
}
else {
reqColl.push(‘GET ‘ + getWebAPIPath() + req.EntitysetName + req.Query + ‘ HTTP/1.1’);
}
reqColl.push(‘Content-Type: application/json’);
reqColl.push(‘OData-Version: 4.0’);
reqColl.push(‘OData-MaxVersion: 4.0’);
reqColl.push(‘Prefer: odata.include-annotations=*’);
reqColl.push(”);
reqColl.push(‘{}’);
});
reqColl.push(‘–batch_fetch–‘);
var mainReq = reqColl.join(‘\r\n’);
//create AJAX request
$.ajax({
type: “POST”,
contentType: “application/json; charset=utf-8”,
datatype: “json”,
async: true,
url: this.getWebAPIPath() + “$batch”,
data: mainReq,
beforeSend: function (xhr) {
//Specifying this header ensures that the results will be returned as JSON.
xhr.setRequestHeader(“Accept”, “application/json”);
xhr.setRequestHeader(“OData-MaxVersion”, “4.0”);
xhr.setRequestHeader(“OData-Version”, “4.0”);
xhr.setRequestHeader(“Prefer”, “odata.include-annotations=*”);
xhr.setRequestHeader(“Content-Type”, “multipart/mixed;boundary=batch_fetch”);
},
success: function (data, textStatus, xhr) {
var results = data.split(“–batch”).slice(1, reqCount + 1);
var recCollection = [];
$.each(results, function (index, data) {
var result = getFormattedData(data);
if (JSON.parse(result).value != undefined) {
recCollection.push(JSON.parse(result).value);
}
else {
recCollection.push(JSON.parse(result));
}
});
batchRequestRetrieveSuccess(recCollection);
},
error: function (xhr, textStatus, errorThrown) {
batchRequestRetrieveError(xhr);
}
});
} catch (e) {
throw new Error(e);
}
}
getWebAPIPath: function () {
return this.getClientUrl() + “/api/data/v9.1/”;
}
getFormattedData: function (value) {
return value.substring(value.indexOf(‘{‘), value.lastIndexOf(‘}’) + 1);
}
Conclusion
With the help of Batch request we can execute multiple Fetch and OData queries to get the results. This increases the performance and reduces the retrieval time.