Here’s a quick and easy way to update a plugin assembly in CRM to give much quicker turn around when developing. Note: this will only work if the plugin is deployed to the database.

Plugin assemblies work just the same as all other entities in CRM, which means you can make API calls to retrieve, create or update them. While there is a bit of effort involved in creating new assembly records in CRM and all the associated plugins, steps, images etc. If you want all that to remain the same and to just simply update the deployed code, all you need to do is the following:

var pluginAssembly = new Entity("pluginassembly")
{
  Id = new Guid(pluginAssemblyId),
  Attributes =
  {
    {"content", Convert.ToBase64String(File.ReadAllBytes(assemblyPath))}
  }
};

Where pluginAssemblyId is the id of the assembly you want to update, assemblyPath is the path to the assembly file on disk that you want to deploy and crmService is an authenticated OrganizationServiceProxy (see: my previous post to see how to initialise one)

If you don’t know the id of the assembly, you can look it up via the name first:

var pluginAssemblies = crmService.RetrieveMultiple(new QueryExpression("pluginassembly")
{
  Criteria = new FilterExpression
  {
    Conditions = {new ConditionExpression("name", ConditionOperator.Equal, name)}
  }
});
var pluginAssemblyId = pluginAssemblies.Entities[0].Id;

Assuming the plugin name is the same as the assembly file name, you could also just pull this from the path:

var name = Path.GetFileNameWithoutExtension(assemblyPath);

I do not particularly like workflows in CRM. Custom workflows in particular. While I concede there are cases where workflows in CRM can work and that certain custom workflows are useful to add additional capabilities to something a power user can control, from my (albeit rather limited) experience, they are often used (or abused?) outside these situations. Especially where a plug-in would be much better suited.

Unfortunately, I have ended up with a project full of them. With over a hundred different processes and 22 custom workflow assemblies referenced by more than 50 them (some referencing multiple), the system is not my perspective of ideal. Regardless, it has fallen to me to upgrade this CRM4 code base and start to get the system in shape for a future move to CRM2013. 

In preparation for this upgrade I was testing the approach to upgrade the workflows and referenced libraries from .NET 3.5, 3 or earlier and replacing references to CRM4 libraries (i.e. Microsoft.Crm.Sdk) with CRM2011 libraries (i.e. Microsoft.Xrm.Sdk etc) and everything that comes with that (replacing CRM4 types: CrmBoolean, CrmNumber, Lookup, DynamicEntity etc). My initial finding was that an in place upgrade of the code would break a deployed workflow. When you deployed the new assembly it seemed to be fine, however new instances of the workflow will fail. If you try deactivate the workflow and edit it you see this:

5

All the steps that reference the custom workflow show an error message and you can't open them to edit or delete them. Other than the error icon, it doesn't provide much information as to what is wrong. To have a look at what was going on, I exported the workflows in a solution and had a look at the XAML. Turns out the workflow itself has references to the input field types and being originally CRM4 based, these are the old CRM types, for example CrmNumber. It seems that this doesn't reconcile when CRM sees the new code that we deployed so it gives up. 

The first fix that came to mind here was to update the XAML to fix these references. This is a bit of a pain, but only really another set of find and replaces. However, it turns out there is a much simpler fix within CRM. All you need to do is open up the workflow (deactivate it if it is active) and then open the properties for the custom activities input (the first Set Properties button in the screen shot above). This will show the inputs for the custom workflow, which should all look the same as before. You don't actually need to change anything here (unless you want to of course), so just save and close out. Again, it looks like nothing has changed and the errors are still there, but hit save and close again. Now just open it up again and all of a sudden the errors are gone.

6

How easy is that! Simply saving the input parameters must trigger CRM to check and update all the parameter types for the workflow, which means the output parameters now match what is expected and the workflow can happily be activated and run. You can confirm this by exporting the workflow again and looking at the XAML.

Awesome, so it seemed like my workflow headaches were going away. That is, until I realised that unlike CRM2011 workflow assemblies, CRM4 did not require the assembly to be signed… and if you sign an assembly it changes the strong name, which means it will not let you provide it as an update for the deployed code, so the above fix was not going to be enough for everything that I was facing... However, how I went about fixing that is a story for another day.

To set the scene, say you are developing a solution for CRM2013 using the Developer Toolkit for Microsoft Dynamics CRM in Visual Studio and wanting to create a asynchronous plugin step. Easy right? Open CRM Explorer, find the entity, add a plugin, fill in the details, set it to run asynchronously and you are away. However, those of you that have used the plugin registration tool will know that there is a pretty handy option when Registering a Step that lets you tell CRM to Delete AsyncOperation (i.e. system jobs) if the StatusCode = Successful:

Register New Step dialog

This is great because it means your system jobs table doesn’t get clogged up with entries when things are going fine. Instead it just tracks when you probably need to look into something. So how do you go about doing this in Visual Studio?

I was in this situation this week and found (let me know if I missed something obvious here) that this isn’t an option using the UI for the developer kit and there is no documentation (that I can find, nothing in the SDK and Google didn’t help me) on how to set this. I considered giving up and having this as an extra step to manually do after deploying the solution, which would be a pain, but decided to spend a bit more time and dig deeper. I decompiled the assembly for the developer toolkit (using dotpeek for those interested) and found that this is a supported, but not advertised option for the step in the RegisterFile.crmregister xml file within the CRMPackage project. All you need to do is add AsyncAutoDelete="true" attribute to the asynchronous step tags(s) in the XML, deploy and you are away laughing.

For example, your RegisterFile.crmregister will end up looking something like this:

<?xml version="1.0" encoding="utf-8"?>
<Register xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/crm/2011/tools/pluginregistration">
   <Solutions>
       <Solution Assembly="Plugins.dll" Id="2318c739-68d9-d896-8cf1-6c3be5a8c238" IsolationMode="Sandbox" SourceType="Database">
           <PluginTypes>
               <Plugin Description="Contact Plug-in" FriendlyName="ContactPlugin" Name="Plugins.ContactPlugin" Id="2618c739-68d9-d896-8cf1-6c3be5a8c238" TypeName="Plugins.ContactPlugin">
                   <Steps>
                       <clear />
                       <Step AsyncAutoDelete="true" CustomConfiguration="" Name="Plugins.ContactPlugin.PostContactUpdate" Description="Post-Operation of Contact Update" Id="92a843f0-68d9-d896-8cf1-6c3be5a8c238" MessageName="Update" Mode="Asynchronous" PrimaryEntityName="contact" Rank="1" SecureConfiguration="" Stage="PostOutsideTransaction" SupportedDeployment="ServerOnly">
                           <Images />
                       </Step>
                   </Steps>
               </Plugin>
           </PluginTypes>
       </Solution>
   </Solutions>
   <XamlWorkflows />
</Register>

While it would be nice to achieve this from the UI, something we might see in a future update, this is at least a pretty simple solution to work with for now.