Word Redundancy Factors
August 14, 2024
CRM Email Router – Emails Stuck at Pending Send
August 14, 2024

Dynamics CRM Paging Part II – Lazy Paging

Posted by TrueNorth

Following on from Dynamics CRM Paging Cookies – Some gotchas!, here is a helper class we use for all our paging needs.

An example of its usage:

[sourcecode language=”csharp”]
const string fetchXml = @”
<fetch mapping=’logical’ count=’5000′ version=’1.0′ page='{0}’ {1}>
<entity name=’account’>
<attribute name=’name’ />
</entity>
</fetch>”;

var accounts = new PagedRetriever<Tuple<Guid, string>>(
entity => Tuple.Create( entity.Id,
(string)entity.Attributes[“name”]),
(page, cookie) => String.Format(fetchXml, page, cookie))
.GetData(service);
[/sourcecode]

 

The utility class has a couple of nice features:

Laziness – Extra pages are only retrieved as they are enumerated
Memory efficient – Entities are converted on the fly, saving a huge amount of memory if a large enumeration is converted to a list

 

Here is the complete code for the Paged Retriever class:

[sourcecode language=”csharp”]
public class PagedRetriever<T>
{
private readonly Func<Entity, T> converter;
private readonly Func<int, string, string> pagedFetchXml;
private readonly bool usePagingCookie;

/// <summary>
/// Utility class to retrieve paged results from CRM
/// </summary>
/// <param name=”converter”>Converts the returned entity to a wrapper type</param>
/// <param name=”pagedFetchXml”>pagedFetchXml takes the pagenumber and the pagingCookie and returns the fetchXml</param>
/// <param name=”usePagingCookie”>Whether the paging cookie should be used. Set to yes for performance if the primary entity id is unique in the resultset.</param>
public PagedRetriever(Func<Entity, T> converter, Func<int, string, string> pagedFetchXml, bool usePagingCookie = true)
{
this.converter = converter;
this.pagedFetchXml = pagedFetchXml;
this.usePagingCookie = usePagingCookie;
}

/// <summary>
/// Get all entities using the standard RetrieveMultiple call
/// </summary>
/// <param name=”service”></param>
/// <returns></returns>
public IEnumerable<T> GetData(IOrganizationService service)
{
return GetData(query => service.RetrieveMultiple(query));
}

/// <summary>
/// Gets all entities using a custom collection producer (eg. a RetrieveByResourcesServiceRequest)
/// </summary>
/// <param name=”retriever”></param>
/// <returns></returns>
public IEnumerable<T> GetData(Func<QueryBase, EntityCollection> retriever)
{
int page = 1;
string pagingCookie = String.Empty;
while (true)
{
var pagedXml = pagedFetchXml(page, pagingCookie);

var entityCollection = retriever(new FetchExpression(pagedXml));
if (entityCollection == null)
{
break;
}

foreach (var convertedEntity in entityCollection.Entities.Select(converter))
{
yield return convertedEntity;
}

if (!entityCollection.MoreRecords)
{
yield break;
}

if (usePagingCookie && !String.IsNullOrEmpty(entityCollection.PagingCookie))
{
pagingCookie = “paging-cookie=’” + System.Web.HttpUtility.HtmlEncode(entityCollection.PagingCookie) + “’”;
}

page++;
}
}
}
[/sourcecode]

Get our Latest Articles in your Inbox

Enjoyed this article? Sign up for our email newsletter and get real-world information on all things Microsoft, cloud and tech. Your information will be shared with MailChimp but no one else, and you can unsubscribe with one click at any time

Sign-Up to Our Newsletter: