Pogo69's Blog

November 5, 2010

Caching Revisited – CRM 4.0 SDK – Advanced Developer Extensions

Filed under: C#, CRM — Tags: , , , , , — pogo69 [Pat Janes] @ 14:07

I was in the middle of a posting on some more advanced concepts regarding the querying of CRM via the new LINQ provider (Microsoft.Xrm.Client) when I discovered another hidden gem.  I’ve now proven the concept and it is so useful, I had to post about it immediately.

Cache Invalidation Plugin

After the release of the new CRM 4.0 SDK, there was a lot of talk generated about the “all or nothing” caching mechanism.  Much of that talk can be found on the Codeplex CRM Accelerator site, due to the Accelerator’s heavy reliance on the xRM client and portal libraries.

There is a hint in a couple of the postings (and somewhere in the xRM documentation I believe) of a Cache Invalidation Plugin; however, no specifics are available and it was subsequently announced that it didn’t get shipped.  Well, guess what?  IT WAS SHIPPED!!  And it’s living inside the same Microsoft.Xrm.Client.dll that we’re all using to help write our fancy new LINQ queries!!

I found it when browsing the assembly with truly magnificent .NET Reflector tool:

Cache Invalidation Plugin

How It Works

In order to use the plugin, you must register it in the CRM using the Plugin Registration tool or similar.  Once the assembly has been registered, you can register steps for whichever entities and messages that you wish.  In my test, I chose to register steps for the Create, Update and Delete messages on the “incident” (Case) entity; mostly due to the fact that our clients have all required some form of access via the Partner Portal to Cases.

If you dig hard enough in the documentation and in postings around the web, you will find sufficient reference to the Portal framework’s Cache Invalidation Handler.  It is mapped to:

http://<portalwebsite>/Cache.axd

So, the next question is, “how does the plugin know how to find the cache invalidation handler?”.

The Portal framework’s CMS entities, contain (among many others) a Web Site entity; that entity has an attribute called ‘Cache Invalidation Handler URL.  Naturally, I initially thought it worked by looking at this attribute, but… it does not.

.NET Reflector to the rescue again!!  When I updated case entities, there was a corresponding error in the log from the plugin telling me that the parameter “configurationXml” cannot be null.  Which is basically telling us that we need to supply a configuration XML when we regsister a step for our plugin assembly.  Digging into Reflector again, I found:

So… we must supply a “Secure Configuration”, but what should it look like?  Back to Reflector:

So, the Configuration constructor throws the “configurationXml” null argument exception that I found in the CRM error log.  It requires that the configuration xml contain a “configuration” root node and builds a name/value collection from the subnodes.  Getting there…

So, we need a Configuration element with the name “invalidateCacheUrl”.  OK, so the final secure configuration XML looks like:

<configuration>
 <invalidateCacheUrl>http//[websiteurl]/Cache.axd</invalidateCacheUrl>
</configuration>

When I registered the steps, I modified the Description so that I could easily identity them in the CRM System Jobs view:

I’ve only done some preliminary testing with the Case (incident) entity, but I have no reason to believe that it won’t work with any or all of the entities; it is the same mechanism that many of us have been using, albeit rather more heavy handed.  But now, we can invalidate per entity instance.

Enjoy!

October 29, 2010

To Cache or Not to Cache – CRM 4.0 SDK – Advanced Developer Extensions

Filed under: CRM — Tags: , , — pogo69 [Pat Janes] @ 00:05

Let’s face it; I’m a bit lucky with respect to my relatively recent introduction to Microsoft Dynamics CRM. I’ve entered the game to find a robust (most of the time AKA when it behaves) and mature product, without many of the restrictions and idiosyncrasies of earlier versions. I’ve still never even seen a CRM 3.0 or earlier system; and between you, me and the world, I’d like to keep it that way. Soon, we’ll see the introduction of CRM2011, which appears to be a developer’s dream – more customisation, packaged “solutions”, more in-depth integration, a more mature client and server object model.

But back to CRM 4.0. The biggest change brought by the most recent release of the CRM 4.0 SDK was the Advanced Developer Extensions (Microsoft xRM *). It allows developers to access CRM entity instances via LINQ; a set of extension methods introduced to C# to allow SQL like syntax querying of an appropriately mapped object model. So, by ditching the old unwieldy QueryExpression syntax, what would formerly have taken dozens of lines of code, can now be expressed by its far more compact (and far more maintainable) equivalent. Something like:

List<Xrm.mrc_coursesubject> subjects =
	(
		from s in DataContext.mrc_coursesubjects
		join sa in DataContext.mrc_coursesubjectallocations on s.mrc_coursesubjectid equals sa.mrc_subjectid.Value
		join cs in DataContext.mrc_courseschedules on sa.mrc_courseid.Value equals cs.mrc_courseid.Value
		where cs.mrc_coursescheduleid == this._courseScheduleId
		select s
	).ToList();

subjects.ForEach(subject =>
		this.ddlSubject.Items.Add(new ListItem(subject.mrc_name, subject.mrc_coursesubjectid.ToString()))
	);

I won’t go further into it in this posting, but keep an eye out for a subsequent posting on some of the idiosyncrasies of CRM specific LINQ queries.

So… caching.

The xRM libraries ship with an inbuilt caching mechanism; also recently released were new versions of what have been termed “Accelerators”.  These complete reference websites are built around a CRM-driven CMS * system.  Because almost everything you see in the Accelerator websites is dynamically generated from entity instances in the CRM, it is very important that the data is cached appropriately.

This, however, has created a number of issues for developers hoping to use the xRM libraries to dynamically display CRM content on their 3rd party websites. Newly created/updated/deleted CRM data is not immediately viewable as previously obtained results are cached.  The documentation makes some reference to the caching mechanisms involved, but not enough to make it immediately obvious how to turn it off, and/or control it to meet your requirements.  While I don’t profess to understand every intricacy of the xRM caching framework, the following outlines what I did to meet our clients’ needs and it has certainly worked for us.

Basically, we pre-process every incoming page request in the ASP.NET Application File, Global.asax; empty functions elided for simplicity and clarity:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;

namespace Namespace.Of.The.Week
{
	public static class Extensions
	{
		public static void RemoveAll(this Microsoft.Xrm.Client.Caching.BaseCache cache)
		{
			foreach (KeyValuePair<string, object> pair in (IEnumerable<KeyValuePair<string, object>>)cache)
			{
				cache.Remove(pair.Key);
			}
		}
	}

	public class Global : System.Web.HttpApplication
	{
		#region Helper Routines
		private static void ClearCache(string entityName)
		{
			var dependency = string.Format("adxdependency:crm:entity:{0}", entityName).ToLower();

			var cache = Microsoft.Xrm.Client.Caching.CacheManager.GetBaseCache();
			cache.Remove(dependency);
		}
		private static void ClearCache()
		{
			Microsoft.Xrm.Client.Caching.CacheManager.GetBaseCache().RemoveAll();
		}
		#endregion

		protected void Application_BeginRequest(object sender, EventArgs e)
		{
			string cacheRemoval = System.Configuration.ConfigurationManager.AppSettings["Cache.Removal"];

			switch (cacheRemoval.ToLower())
			{
				case "all":
					// clear all cache items for the following entities
					ClearCache();
					break;

				case "entity":
					string[] entities = System.Configuration.ConfigurationManager.AppSettings["Cache.Removal.Entities"].Split(new char[] { ',' });
					foreach (string entity in entities)
					{
						ClearCache(entity);
					}
					break;
			}
		}

		...
	}
}

* xRM – Anything (x) Relationship Management

* CMS – Content Management System

Blog at WordPress.com.