Wednesday, September 02, 2009

Supported Record Counter For Microsoft Dynamics CRM 4.0

I've already published code of record counter for Microsoft Dynamics CRM 4.0 in this post. Those record counter worked but it had some issues (lookups, advanced finds, form assistant).

I've upgraded the code and now it works perfectly.


UPD: according to comment of Moti Mendelovich to this thread i've one more time updated the code of the plugin and now it works even count of records in fetch response greater then 5000.

Code:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Win32;
using System.Xml;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using Microsoft.Crm.Sdk.Metadata;

namespace RecordCounter
{
public class ExecuteHandler : IPlugin
{

#region IPlugin Members

public void Execute(IPluginExecutionContext context)
{
if (context.Depth != 1) //To calculate count of pages and records another one fetch will be executed
return;//so to avoid infinite loops i need to check the depth of request - if it more then 2 - return

if (context.MessageName == "Execute" && context.InputParameters.Contains("FetchXml"))
{
XmlDocument indoc = new XmlDocument();
indoc.LoadXml((string)context.InputParameters["FetchXml"]);

//Retrieve name of entity to display
string entityName = indoc.SelectSingleNode("//fetch/entity").Attributes["name"].InnerText;

if (entityName == EntityName.savedquery.ToString() ||//To make Advanced Find Work
entityName == EntityName.businessunitnewsarticle.ToString() ||//To make Literature work
entityName == EntityName.resource.ToString() ||//To make Service calendar work
entityName == EntityName.systemuser.ToString() ||//To make Service calendar work
entityName == EntityName.equipment.ToString() ||//To make Service calendar work
entityName == EntityName.asyncoperation.ToString())
return;

//Creation of Metadata service - it will be need for retrieving of main attribute of entity
IMetadataService mservice = context.CreateMetadataService(false);

RetrieveEntityRequest request = new RetrieveEntityRequest();
request.RetrieveAsIfPublished = false;
request.LogicalName = entityName;
request.EntityItems = EntityItems.EntityOnly;
string primaryFieldName = ((RetrieveEntityResponse)mservice.Execute(request)).EntityMetadata.PrimaryField;
//CrmService Creation
ICrmService crmService = context.CreateCrmService(true);

//Count of records by page - for calculation of pages count
int pagecount = int.Parse(indoc.DocumentElement.Attributes["count"].InnerText);

//I remove this attributes for retrieve of all records in current view
indoc.DocumentElement.Attributes.Remove(indoc.DocumentElement.Attributes["count"]);
indoc.DocumentElement.Attributes.Remove(indoc.DocumentElement.Attributes["page"]);

foreach (XmlNode node in indoc.SelectNodes("//fetch/entity/attribute"))
indoc.SelectSingleNode("//fetch/entity").RemoveChild(node);

foreach (XmlNode node in indoc.SelectNodes("//fetch/entity/order"))
indoc.SelectSingleNode("//fetch/entity").RemoveChild(node);

foreach (XmlNode node in indoc.SelectNodes("//fetch/entity/link-entity"))
foreach(XmlNode subnode in node.SelectNodes("./attribute"))
node.RemoveChild(subnode);

XmlAttribute aggrAttr = indoc.CreateAttribute("aggregate");
aggrAttr.Value = "true";
indoc.DocumentElement.Attributes.Append(aggrAttr);

XmlNode field = indoc.CreateNode(XmlNodeType.Element, "attribute", null);

XmlAttribute nameAttr = indoc.CreateAttribute("name");
nameAttr.Value = string.Format("{0}id", (entityName == EntityName.activitypointer.ToString() ? "activity" : entityName));
field.Attributes.Append(nameAttr);

XmlAttribute aggregateAttr = indoc.CreateAttribute("aggregate");
aggregateAttr.Value = "count";
field.Attributes.Append(aggregateAttr);

XmlAttribute aliasAttr = indoc.CreateAttribute("alias");
aliasAttr.Value = "C";
field.Attributes.Append(aliasAttr);

indoc.SelectSingleNode("//fetch/entity").AppendChild(field);

//Xml of full result (without paging)
string fullResult = crmService.Fetch(indoc.OuterXml);

XmlDocument fullResultDocument = new XmlDocument();
fullResultDocument.LoadXml(fullResult);

//Total record count by fetch
int totalRecordCount = int.Parse(fullResultDocument.SelectSingleNode("//resultset/result/C").InnerText);
int totalPageCount = (totalRecordCount / pagecount) + ((totalRecordCount % pagecount) == 0 ? 0 : 1);

string result = string.Format("Total records = {0}, Total pages = {1}", totalRecordCount, totalPageCount);

//Result XML which is the result shown in Grid
XmlDocument outdoc = new XmlDocument();
outdoc.LoadXml((string)context.OutputParameters["FetchXmlResult"]);

//Creation of record which will show totals
XmlNode ResultNodeText = outdoc.CreateNode(XmlNodeType.Element, primaryFieldName, null);
ResultNodeText.InnerText = result;

XmlNode ResultNodeId = outdoc.CreateNode(XmlNodeType.Element, string.Format("{0}id", (entityName == EntityName.activitypointer.ToString() ? "activity" : entityName)), null);
ResultNodeId.InnerText = Guid.Empty.ToString();

XmlNode res = outdoc.CreateNode(XmlNodeType.Element, "result", null);
res.AppendChild(ResultNodeText);
res.AppendChild(ResultNodeId);

XmlNode ResultNodeType;

//Following code repair icon for record counter icon
if (entityName == EntityName.activitypointer.ToString())
{
ResultNodeType = outdoc.CreateNode(XmlNodeType.Element, "activitytypecode", null);
ResultNodeType.InnerText = "4212";
res.AppendChild(ResultNodeType);
}

//This code repair report view
if (entityName == EntityName.report.ToString())
{
ResultNodeType = outdoc.CreateNode(XmlNodeType.Element, "reporttypecode", null);
ResultNodeType.InnerText = "1";
res.AppendChild(ResultNodeType);
}

//Adding record with label of count of pages and records as a first record in recordset
outdoc.SelectSingleNode("//resultset").InsertBefore(res, outdoc.SelectSingleNode("//resultset").FirstChild);
context.OutputParameters["FetchXmlResult"] = outdoc.OuterXml;
}
}

#endregion

}
}


Registration of the step for this plugin:



And screenshots:

Advanced find:



Lookup:



Public and private views:



Quick find:



Form assistant:



Auto resolve lookup view:



Source code you can download here.