Posted on 27. August 2013

Microsoft. Xrm. Client (Part 3a): CachedOrganizationService

In this series we have been looking at the Developer Extensions provided by the Microsoft.Xrm.Client assembly:

Part 1 - CrmOrganizationServiceContext and when should I use it?

Part 2 - Simplified Connection Management & Thread Safety

This 3rd part in the series demonstrates when and how to use the CachedOrganizationService.

When writing client applications and portals that connect to Dynamics CRM there are many situations where you need to retrieve data and use it in multiple places. In these situations it is common practice to implement a caching strategy which although can be easy implemented using custom code can quickly add complexity to your code if you're not careful.

The CachedOrganizationService provides a wrapper around the OrganizationServiceProxy and provides a caching service that is essentially transparent to your code with the cache being automatically invalidated when records are updated by the client. The CachedOrganizationService inherits from OrganizationService and uses the same CrmConnection instantiation so you can almost swap your existing OrganizationService with a CachedORganizationService so that your code can benefit from caching without any changes. There are always some pieces of data that you don't want to cache, and so you will need to plan your caching strategy carefully.

Using an CachedOrganizationService

You have two choices when it comes to instantiating the objects required:

  1. Manual Instantiation – Full control over the combination of the CrmConnection, OrganizationService & OrganizationServiceContext
  2. Configuration Manager Instantiation – App/Web.config controlled instantiation using a key name.

Part 3b will show how to use the Configuration Management, but for now we'll explicitly instantiate the objects so you can understand how they work together.

CrmConnection connection = new CrmConnection("CRM");       
using (OrganizationService service = new CachedOrganizationService(connection))
using (CrmOrganizationServiceContext context = new CrmOrganizationServiceContext(service))

Using the CachedOrganizationService to create your Service Context gives your application automatic caching of queries. Each query results is stored against the query used and if when performing further queries, if there is a matching query, the results are returned from the cache rather than using a server query.

Cached Queries

In the following example, the second query will not result in any server request, since the same query has already been executed.

QueryByAttribute request = new QueryByAttribute(Account.EntityLogicalName);
request.Values.Add("Big Account");
request.ColumnSet = new ColumnSet("name");

// First query will be sent to the server
Account acc1 = (Account)service.RetrieveMultiple(request).Entities[0];

// This query will be returned from cache
Account acc2 = (Account)service.RetrieveMultiple(request).Entities[0];

If another query is executed that requests different attribute values (or has different criteria), then the query is executed to get the additional values:

QueryByAttribute request2 = new QueryByAttribute(Account.EntityLogicalName);
request.Values.Add("Big Account");
request.ColumnSet = new ColumnSet("name","accountnumber");

// This query will be sent to the server because the query is different
Account acc3 = (Account)service.RetrieveMultiple(request).Entities[0];

Cloned or Shared

By default, the CachedOrganizationSevice will return a cloned instance of the cached results, but it can be configured to return the same instances:

((CachedOrganizationService)service).Cache.ReturnMode =

QueryByAttribute request = new QueryByAttribute(Account.EntityLogicalName);
request.Values.Add("Big Account");
request.ColumnSet = new ColumnSet("name");

// First query will be sent to the server
Account acc1 = (Account)service.RetrieveMultiple(request).Entities[0];

// This query will be returned from cache
Account acc2 = (Account)service.RetrieveMultiple(request).Entities[0];
Assert.AreSame(acc1, acc2);

The assertion will pass because a ReturnMode of 'Shared' will return the existing values in the cache and not cloned copies (the default behaviour).

Automatic Invalidated Cache on Update

If you then go on to update/delete and entity that exists in a cached query result, then the cache is automatically invalidated resulting in refresh the next time it is requested.

Coupling with CrmOrganizationServiceContext

In Part 1 we saw that the CrmOrganizationServiceContext provided a 'Lazy Load' mechanism for relationships, however it would execute a metadata request and query every time the relationship Entity set was queried. When this is coupled with the CachedOrganizationService it gives us the complete solution. In the following example, we perform two LINQ queries against the Account.contact_customer_accounts relationship, the first returns all the related contacts (all attributes), and the second simply retrieves the results from the cache. You don't need to worry about what is loaded and what is not.

// Query 1
Console.WriteLine("Query Expected");
Xrm.Account acc = (from a in context.CreateQuery()
                   where a.Name == "Big Account"
                   select new Account
                       AccountId = a.AccountId,
                       Name = a.Name,

// Get the contacts from server
Console.WriteLine("Query Expected");
var accounts = (from c in acc.contact_customer_accounts
                select new Contact
                    FirstName = c.FirstName,
                    LastName = c.LastName

// Get the contacts again - from cahce this time
Console.WriteLine("No Query Expected");
var accounts2 = (from c in acc.contact_customer_accounts
                 select new Contact
                     FirstName = c.FirstName,
                     LastName = c.LastName,
                     ParentCustomerId = c.ParentCustomerId

Thread Safety

Provided you are using the CachedOrganizationService with the CrmConnection class, then all the same multi-threading benefits apply. The Client Authentication will only be performed initially and when the token expires, and the cache automatically handles locking when being access by multiple threads.

Pseudo Cache with OrganizationServiceContext.MergeOption = AppendOnly

The OrganizationServiceContext has built in client side tracking of objects and a sort of cache when using a MergeOption Mode of AppendOnly (the default setting). With MergOption=AppendOnly once an entity object has been added to the context, it will not be replaced by a instance on subsequent LINQ queries. Instead, the existing object is re-used so that any changes made on the client remain. This means that even if a new attribute is requested and the CachedOrganizationService executes the query accordingly, it will look as though it hasn't been a new query because the OrganizationServiceContext still returns the object that it is currently tracking.

// Query 1
Xrm.Account acc = (from a in context.CreateQuery()
                   where a.Name == "Big Account"
                   select new Account
                       AccountId = a.AccountId,
                       Name = a.Name,

Assert.IsNull(acc.AccountNumber); // We didn’t request the AccountNumber

// Query 2
// Because there is an additional attribute value requested, this will query the server
Xrm.Account acc2 = (from a in context.CreateQuery()
                    where a.Name == "Big Account"
                    select new Account
                        AccountId = a.AccountId,
                        Name = a.Name,
                        AccountNumber = a.AccountNumber

Assert.AreSame(acc, acc2); // MergeOption=AppendOnly preserves existing objects and so the first tracked object is returned
Assert.IsNull(acc.AccountNumber); // Account Number will be null even though it was returned from the server

This can lead to the conclusion that the CachedOrganizationService isn't detecting that we want to return a new attribute that wasn't included in the first query, but it actually isn't anything to do with caching since the OrganizationServiceContext will behave like this even if there was no CachedOrganizationService in use. If you were to use a MergeOption of NoTracking, PreserveChanges or Overwrite changes you wouldn't see the above behaviour because the 2nd query would always return a new instance of the account with the AcccountNumber attribute value loaded.

Next in this series I'll show you how to configure the CachedOrganisationSevice and OrganizationServiceContext using the web/app.config.


Posted on 22. August 2013

Multi-Entity Search with SparkleXRM

The new tablet client for Dynamics CRM 2013 has a fantastic looking multi-entity search but it is not yet available in the Web Client.

I thought this would be a good opportunity to create another SparkleXRM sample to achieve a similar feature with Dynamics CRM 2011.

You can check out the sample by installing the managed solutions:

Once installed, it creates a sitemap entry that runs shows the Multi Entity Search HTML Web resource. By default, it'll show the Account, Contact, Lead, Activity & Opportunity entities, but can show other entities by passing parameters.

Each entity grid shows the 'Quick Find' view for the given entity and displays the head and column names in the user's chosen language. The grids are fixed widths, but will wrap according to the screen width available.

The sample shows the following features:

  1. Using the Metadata Query SDK to retrieve entity & attribute types and display names in the user's chosen language.
  2. Grids displaying the page size defined in the user's settings.
  3. Using the grid data binder and parsing fetchxml/layoutxml
  4. Rendering grids with clickable links to entity records.
  5. MVVM binding with asynchronous queries

It achieves all of this in very few lines of code. You can take a look at the sample code on GitHub:


Posted on 17. August 2013

Microsoft. Xrm. Client (Part 2): Simplified Connection Management & Thread Safety

In the last post in this series I showed you the difference between the standard OrganizationService and the Microsoft.Xrm.Client.CrmOrganizationService. Continuing with the subject of elaborating on the 'Developer Extensions' part of the Dynamics CRM SDK this post describes more about the 'Simplified Connection Management' features.

When accessing the IOrganizationService Inside a Plugin or Workflow all connection management is handled for you by the existing context:

// Obtain the organization service reference.
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

(See the SDK Sample for more on how to do this)

There are no server Urls and usernames or passwords to worry about since the code is already running in the context of Dynamics CRM. 

Authentication with no help

When it comes to writing .NET Client or Portal Applications there isn't an existing context to use and so connection details must be stored and used when connecting to Dynamics CRM. The most basic approach using the standard Microsoft.Xrm.Sdk assembly would be:

// Active Directory
Uri organizationUri = new Uri("http://servername/orgname");
ClientCredentials credentials = new ClientCredentials(); 
credentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
OrganizationServiceProxy proxy = new OrganizationServiceProxy(organizationUri, null, credentials, null);
// Claims Base Authentication
Uri organizationUri = new Uri("");
AuthenticationCredentials authCredentials = new AuthenticationCredentials();
authCredentials.ClientCredentials.UserName.UserName = username;
authCredentials.ClientCredentials.UserName.Password = password;

// Dynamics CRM Online requires device credentials as well
ClientCredentials deviceCredentials = new AuthenticationCredentials();
deviceCredentials.ClientCredentials.UserName.UserName = deviceUsername;
deviceCredentials.ClientCredentials.UserName.Password = devicePassword;

ClientCredentials credentials = authCredentials.ClientCredentials;
OrganizationServiceProxy proxy = new OrganizationServiceProxy(organizationUri, null, credentials, deviceCredentials);

Whilst this technique works well, you need to code around the following issues:

  1. Different Connection Types - Different code is required for Claims Based/Online/Active Directory authentication, so your code will need to detect which environment and authenticate accordingly.
  2. Connection Dialog - Creating a user interface for entering server addresses, user credentials and organization selection is a complex area with many possible configurations depending on your target environment.
  3. Thread Safety - The OrganizationServiceProxy is not thread-safe. Each thread will need a separate instance and downloading metadata & authenticating each time results in slow response times.
  4. Token Expiry - Claims Based authentication creates an authentication token that has a finite lifetime. Each time your code uses an OrganizationServiceProxy that has been cached, it will have to check and renew the token if required.
  5. Enable Proxy Types – If you need early bound support you'll need to remember to call 'EnableProxyTypes'

There are some helper classes in the SDK that help you but it's far from simple:

  1. Different Connection Types - Microsoft.Crm.Services.Utility.DeviceIdManager provides an easy way to automatically retrieve or create new device credentials when authenticating with Microsoft Dynamics CRM Online. See the authentication helper sample in the SDK.
  2. Connection Dialog - Microsoft.Crm.Sdk.Samples.ServerConnection class provides a way of prompting the user for Server information, but only works on the Command Line - ServerConnection Helper Class . There is also from Tanguy that provides a User interface for collection connection information
  3. Token Expiry - Microsoft.Crm.Sdk.Samples.ManagedTokenOrganizationServiceProxy provides a wrapped OrganizationServiceProxy that automatically checks and renews the Security Token each time it is created.
  4. Thread Safety - Microsoft.Xrm.Sdk.Client.IServiceManagement<IOrganizationService> provides a threadsafe way of getting an instance of an OrganizationServiceProxy. See 'Improve Service Channel Allocation Performance'. You can learn more about multi-threading with the OrganizationServiceProxy from Crm In The Field.

There is an easy way - Microsoft.Xrm.Client

Not to be confused with the Microsoft.Xrm.Sdk.Client, the Microsoft.Xrm.Client namespace is a single library designed specifically to make the Developer's task for writing rich .NET Client and ASP.NET applications easy. In part 1, we've already learned about the enhanced CrmOrganizationServiceContext, but this library also provides a simple way of connecting to Dynamics CRM that addresses all of the issues described above.

Connection Strings

Each connection can easily be expressed as a single connection string stored in the App/Web.Config, collected through a simple to use Connection Dialog or stored by your own custom configuration mechanism. This connection string is uses to create a CrmConnection, and from that you can create a OrganizationService.

// Connect with Dialog
ConnectionDialog connectionDialog = new ConnectionDialog();
bool? connected = connectionDialog.ShowDialog();
if (connected!=null && connected.Value)
string connectionString = connectionDialog.ConnectionString;
using (OrganizationService proxy = new OrganizationService(CrmConnection.Parse(connectionString)))
WhoAmIRequest request = new WhoAmIRequest();
WhoAmIResponse response = (WhoAmIResponse)proxy.Execute(request);

// Connect from App/Web.config
CrmConnection connection = new CrmConnection("CRM");
using (OrganizationService proxy = new OrganizationService(connection))
WhoAmIRequest request = new WhoAmIRequest();
WhoAmIResponse response = (WhoAmIResponse)proxy.Execute(request);

In your app/web.config file you can then add set of named connection strings. This works well for ASP.NET portal applications.

<add name="CRM" connectionString="..."/>

Thread Safety and Token Expiry

The good news is that all the token expiry and thread safety is handled for you automatically provided you reuse an instance of the CrmConnection each time you create an OrganizationService:

using (OrganizationService proxy = new OrganizationService(cachedConnection))
  using (CrmOrganizationServiceContext ctx = new CrmOrganizationServiceContext(proxy))

The authentication and metadata requests will only happen on the first use and then once the auth token has expired (if you are using Claims Based/Online authentication).

CrmConnection Settings

As well as the connection string, you can also change the behaviour of the CrmConnection using the following settings:

  1. ProxyTypesEnabled – Defaults to true and controls if 'EnableProxyTypes' is called for your on your OrganizationServiceProxys.
  2. Timeout – You can control the timeout of your connections otherwise the default OrganizationServiceProxy timeout is use of 2 minutes.
  3. UserTokenExpiryWindow – null by default, but controls how often your security token should be renewed for Claims Based/Online authentication. If you leave as null, the authentication token 'ValidTo' will be used to define when the token should be renewed. Only set this value if you want to renew before the token has expired. Use with caution!
  4. ServiceConfigurationInstanceMode – Defaults to PerName, but can be modified to be PreInstance, PerRequest or Static. I recommend sticking with the default. If you use 'PerRequest' you will get a metadata and authentication request for every single proxy!
If you are writing .NET client or ASP.NET that connect to Dynamics CRM there is no reason not to use this library! Next up in part 3 is the super cool client side data caching mechanism provided by Microsoft.Xrm.Client.


Posted on 12. August 2013

Microsoft.Xrm.Client Part 1: CrmOrganizationServiceContext and when should I use it?

The Dynamics CRM SDK documentation is amongst the best I've seen for any business application. It provides numerous code examples and walk through ranging from the basics all the way through to advanced topics.

One of the areas that perhaps needs a little more clarification is the 'Developer Extensions' topic. This post is part 1 of a series on this SDK 'blind spot' so you can get the most out of this value part of the SDK.

The Microsoft.Xrm.Client namespace comes from the assembly of the same name 'microsoft.xrm.client.dll'. It is not available to plugin or workflow code and is designed specifically for use in Windows .NET clients or ASP.NET clients that communicate with Dynamics CRM.

It provides the following key features; each will be a topic of separate blog post.

  1. CrmSvcUtil & OrganizationServiceContext enhancements such as lazy loading
  2. Simplified Connection Management with Connection Dialog UI
  3. Client Side caching extensions
  4. Utility Extension functions for common tasks to speed up client development
  5. Organization Service Message utility functions to make it easy to call common messages such as BulkDelete, Add Member to Team etc.
  6. Objects to support the Microsoft.Xrm.Portal extensions

One area that I do get asked about quite frequently is the difference between the CrmOrganizationServiceContext and your common-all-garden OrganizationServiceContext. This question is the subject of this post.

Both of these classes implement the IOrganziationServiceContext interface and so once created they can be used interchangeably, but before you do this it is important to understand what is going on and why.

To use the CrmOrganizationServiceContext correctly, your journey should start with the Developer Extensions to the CrmSvcUtil. Rather than using the standard CrmSvcUtil flavour, you need to modify your command to include the following:

CrmSvcUtil.exe /codeCustomization:"Microsoft.Xrm.Client.CodeGeneration.CodeCustomization,Microsoft.Xrm.Client.CodeGeneration"  /url:http://<server>/<org>/XRMServices/2011/Organization.svc /out:"Xrm.cs" /namespace:Xrm /serviceContextName:XrmServiceContext

This will give you a very familiar looking early bound code file but with some critical differences:

  1. The Entity classes now inhertit from Microsoft.Xrm.Client.CrmEntity and not Microsoft.Xrm.Sdk.Entity
  2. The generated Service Context will inhertit from Microsoft.Xrm.Client.CrmOrganizationServiceContext and not Microsoft.Xrm.Sdk.Client.OrganizationServiceContext

The XrmServiceContext has some new constructors that allow you to create the OrganizationService from the simplified connection API (Part 2 of this series), but apart from that there aren't really any differences. The magic is what is going on in the new base classes that allow Lazy Loading of the Relationship attributes.

In order to access a relationship property using the standard OrganizationServiceContext you would need to use:

ctx.LoadProperty(ParentAccount, (a) => a.contact_customer_accounts);

Note: This is using the LINQ expression to specify the relationship – which is much easier than the 'traditional approach' of specifying the relationship name as string!

..but using the CrmOrganizationServiceContext, you can now simply query the relationship and the values will be automatically 'lazy loaded' for you. It is called 'lazy' because it will only load when it is first called:

var relatedContacts = (from c in acc.contact_customer_accounts select c);

This allows you to simply query the object graph as though it was all in memory on the client already without worrying if it has been already loaded. With this automatic expand behaviour comes with some important points to watch for:

  1. Every time you perform a LINQ query against a lazy loaded property, the CrmOrganizationService will first query the relationship metadata to and then it will return all related entities using a Retrieve request with the RelatedEntitiesQuery populated. It makes sense to use this technique only when you need to load all related entities as part of a client side caching strategy (see Part 3).
  2. Any 'where' filters will be applied on the client side once all the related entities are retrieved. If you need to perform server side filtering, it would best to use a standard OrganizationServiceContext.CreateQuery<T>
  3. Joins will be applied re-cursively by repeatedly querying each relationship – so again if you need complex joins and don't want all the entities on the client use CreateQuery<T> and a server side query.
  4. Any LINQ projections you use in the query will not be used since all attributes are returned for the related entities. The idea again is that a caching strategy is used so that any filters can be applied on the client side without re-requesting the data from the server.

The LINQ query above will result in the following requests being made:

First the relationship query:

<s:Envelope xmlns:s="">
    <Execute xmlns="" xmlns:i="">
      <request i:type="a:RetrieveRelationshipRequest" xmlns:a="">
        <a:Parameters xmlns:b="">
            <b:value i:type="c:guid" xmlns:c="">00000000-0000-0000-0000-000000000000</b:value>
            <b:value i:type="c:boolean" xmlns:c="">false</b:value>
            <b:value i:type="c:string" xmlns:c="">contact_customer_accounts</b:value>
        <a:RequestId i:nil="true" />

And then the actual query:

<s:Envelope xmlns:s="">
    <Execute xmlns="" xmlns:i="">
      <request i:type="a:RetrieveRequest" xmlns:a="">
        <a:Parameters xmlns:b="">
            <b:value i:type="a:EntityReference">
              <a:Name i:nil="true" />
            <b:value i:type="a:ColumnSet">
              <a:Columns xmlns:c="" />
            <b:value i:type="a:RelationshipQueryCollection">
                  <a:PrimaryEntityRole i:nil="true" />
                <b:value i:type="a:QueryExpression">
                    <a:Columns xmlns:c="" />
                    <a:Conditions />
                    <a:Filters />
                  <a:LinkEntities />
                  <a:Orders />
                    <a:PagingCookie i:nil="true" />
        <a:RequestId i:nil="true" />

This functionality is specific built for building Client applications that connect to Dynamics CRM, to make displaying CRM data to the user easier. It is not intended for use in Plugins or Workflow Activities.

Next up is Part 2 on the simplified connection management extensions.


Posted on 9. August 2013

User Impersonation in Plugins, Workflow and Dialogs

I've recently had a few questions around the UserId and InitiatingUserId properties of the execution context, so I thought I'd attempt to clear up any confusion.

The simple fact around user impersonation in Dynamics CRM is that it isn't simple! I could write pages on the subject but instead here are 2 grids that explain everything you need to know. The key point to remember is that these User Identities have very little to do with the Windows Authentication Identity (service accounts etc.) – they are all GUID's that refer to User records within your Dynamics CRM Organisation.

Impersonation in Plugins

The following grid shows the various user identities present for Plugins. 'Triggering User' refers to the logged in user who saves the record in Dynamics CRM and triggers a Plugin to fire. It's also worth noting that offline plugins will fire once offline as shown and then *again* on the server.

Impersonation in Dialogs and Workflows

The following grid shows the various user identities present for Workflows and Dialogs. The interesting thing here is the difference between Parent and Child Automatic Workflows.

Hope that settles the matter - I'm sure it won't!


Posted on 8. August 2013

jQuery and jQuery UI with Dynamics CRM 2011 & 2013

Since I’ve been converting Silverlight web resources over to Html & JavaScript and working on , I’ve worked extensively with jQuery and jQuery-UI.

In the early days of Dynamics CRM 2011, you could use both these libraries without a problem, but with the Activity Feeds solution an instance of jQuery appeared that interfered with your custom scripts.
This is still the case in Dynamics CRM 2013 and so here are some simple steps to ensure your libraries are safe and will co-exist with other instances from other solutions.

1. Decide on a custom ‘namespace’ for your jQuery library. I am using ‘xrmjQuery’

2. On the end of your jquery.js script add the following line:

/* jQuery script goes here */
window.xrmjQuery = jQuery.noConflict(true);

3. Inside your jquery_ui.js script (notice the ‘-‘ has been changed to an underscore since CRM doesn’t allow them in web resource names), wrap the whole file in the following lines:

(function ($,jQuery) {
	/*! jQuery UI Goes here */

4. Inside your JavaScript web resource that use jQuery and jQuery-UI, wrap your code in the following:

// Your Javascript goes here and can reference $ as usual
// e.g. var someField = $('#fieldName');

This technique is called encapsulation and namespacing of jQuery.

My friend Carsten also has a blog post on a similar theme. uses the same technique and namespace is also ‘xrmjQuery’, so if you would like to quickly get access to the jQuery libraries in Dynamics CRM, you can install the SparkelXrm managed solution and include the web resource named ‘sparkle_/js/SparkleXrmUI_Dependancies.js’ – this is a single library that has both jQuery, jQueryUI as well as a few other goodies such as Knockout JS!



Posted on 7. August 2013

Compare customisations between two Dynamics CRM Organisations

Your TEST customisations are always the same as your PRODUCTION customisations, because no changes are made direct to PRODUCTION – right? Really? Are you sure?!

We all know that customisations shouldn't be made directly to a production server, but rather made in development and then promoted through the various instances. Sometimes there are circumstances where this rule has to be broken. Naturally, this can lead to customisations not be retro-fitted back into Development/Test servers.

If you want to check that both organisations are in sync, you can follow these instructions that uses the SolutionPackager tool that is shipped with the Dynamics CRM SDK.

  1. Download the Dynamics CRM 2011 SDK -
  2. Extract the zip to a folder such as C:\SDK
  3. Create folder for your customisations to live:
    1. C:\Customisations\Production
    2. C:\Customisations\Test
  4. Copy the 'solutionpackager.exe' from 'C:\SDK\bin' into 'C:\Customisations'
  5. Download the default solution for Production and Test and place in their respective folders.

    Settings->Customizations->Customize the System->Export Solution

    You could use another unmanaged solution of your choice – but it must be the same on both Production and Test
  6. In the C:\Customisations folder create a batch file named 'Extract Customisations.bat':

    solutionpackager /action:Extract /zipfile:Production\ /folder:Production\Solution

    solutionpackager /action:Extract /zipfile:Test\ /folder:Test\Solution


  7. Run the batch file by double clicking on it in Windows Explorer.
  8. You should now have a full set of folders representing all your entities, forms etc. in the Solution folders.
  9. All that remains is to compare the two using a visual compare tool such as Beyond Compare or WinDiff.Exe (203.33 kb).
    You can also use this technique to add your customisations to a source control system such as Team Foundation Server

Let's hope you don't find anything too major!

P.S. There is a tool to do this - but I find using the solutionpackager to be far easier and accurate.

Posted on 7. August 2013

Ribbon ‘XML’ Workbench for Dynamics CRM 2013 with a silent ‘XML’!

After much deliberation and consultation, I have decided to stick with the name 'Ribbon Workbench' rather than rename to CommandBar Workbench. Here are some of the reasons behind this decision:

  1. The Command Bar in CRM 2013 is still defined with RibbonXml, but rendered differently. A DisplayRule is used to exclude buttons from the CommandBar.
  2. The Ribbon is still present on non-refresh entities and in Outlook so it's important to still have support for Ribbon editing.
  3. Since the Ribbon Xml remains unchanged, it's important to understand and differentiate between Tabs/Groups and Layouts.
  4. The Ribbon Workbench is already an established 'brand' than people know about.
  5. So until we have CommandBar XML in a future version of Dynamics CRM – it'll always be the Ribbon 'XML' Workbench with a *silent* 'XML'!

The new version has the following improvements:

  • Dynamic switching between the Command Bar and Ribbon view
  • UI refreshed to match Dynamics CRM 2013
  • Performance Optimisations
  • Support for new Dynamics CRM 2013 schema
  • For those of you with access to the Dynamics CRM 2013 beta - the Ribbon Workbench for Dynamics CRM 2013 beta is available for download here:


    Posted on 6. August 2013

    Dude, Where's my Site Map Editor?

    Since the re-org of Pinpoint, my favourite site map editor (the one written in Silverlight by the Microsoft Dynamics Team) has dissapeared.

    It used to be here:

    In case you are also looking for it, I've put a copy here: (1.04 mb)

    If I find it's new home, I'll update this post.