Monday, December 22, 2014

Speed Up CRM Development Using SideWaffle

Recently I had the pleasure of attending a meeting of the Northeast Wisconsin Developers User Group (NEWDUG) where Windows Platform Development MVP and fellow Wisconsinite Carl Schweitzer presented on SideWaffle. If you aren't familiar with SideWaffle it's a Visual Studio extension which provides a number of useful project and item templates to speed up development. All the out of the box items are great but I thought there are lots of instances when developing against Dynamics CRM where you could start from a template (like plugins and workflows). This could provide a pre-built project with all the proper references along with any helper code and class templates that contains all the boiler plate code you end up copying and pasting from some previous work. I'm going to walk through creating project and item (class file) templates for a CRM 2015 custom workflow activity.

As a prerequisite you'll need to download and install the Visual Studio 2013 SDK (or the 2012/2010 version if that's what you're developing in). Also download and install the SideWaffle Template Pack from the Visual Studio Gallery.

1

Creating a base workflow project template

Create a new VSIX project. I'll be referring to this project as the Item Template Project as ultimately this is where the template for the workflow class file will go and later on a second project will be created and added to this solution.

2

At the very least populate the 'Author' field but it probably wouldn't hurt to fill in some of the other information.

3

Install ‘Template-Builder’ with the ‘-pre’ option from the Package Manager Console. Close the readme.txt file when it appears – it didn’t actually do what it said it did but we’ll fix that later.

4

Create a new Class Library project in the solution. This will be the template for the CRM workflow project, I'll refer to this as the Project Template Project  (I can't think of anything better - sorry) going forward.

5

I'm going to be targeting CRM 2015 but you could easily be targeting 2013 or 2011 (based on which NuGet packages are used later), but if you are building for 2015 you'll need to update the project properties and change the Target Framework to .NET Framework 4.5.2 to match what the SDK assemblies are built on. If this isn't an option install the .NET Framework 4.5.2 Developer Pack and it should then show up.

Make sure the new Project Template Project is set so that is doesn’t Build. Build menu -> Configuration on both Debug and Release uncheck the Build option.

6

Add the appropriate Dynamics CRM NuGet packages to the Project Template Project.
Add the Microsoft Dynamics CRM 2015 SDK core assemblies:
  • Install-Package Microsoft.CrmSdk.CoreAssemblies
Since this is for a workflow, also add the Microsoft Dynamics CRM 2015 SDK workflow assembly:
  • Install-Package Microsoft.CrmSdk.Workflow
If you add these from the console, make sure you target the correct project using the ‘-project [project template project name]' option.

If you are creating a template for CRM 2011 or CRM 2013/SP1 choose an earlier version of the SDK assemblies which specifies the particular version you want to target. You can find the earlier versions on the NuGet landing page (bottom) for the packages.

Right click on the new project Add -> New Item -> Extensibility -> SideWaffle -> SideWaffle Project Template Files. No need to change the name here. This should add a folder and a few files to the project.

7

The Project Template Project needs to be signed in order to deploy to CRM so go ahead and do so from the project properties.

Here is what you should have so far:

8

Open up ‘_project.vstemplate.xml’ under the ‘_Definitions’ folder. Update the 'Name' and 'Description' elements to something descriptive. If you are graphically inclined you can swap out the stock image that will show up in Visual Studio for one of your own. Replace the image in the project but keep the file name the same as there is functionality to exclude the image and the other supporting files from the project when it generated from the template. This would be a 32px by 32px () image and keep in mind when choosing images how it will appear depending on the user’s theme (Light vs. Dark) that is selected. Visual Studio inverts the images colors based on the theme that is active, so what looks good in one might not look great in the other.

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
  <TemplateData>
    <Name>CRM 2015 (7.0.X) Workflow Project</Name>
    <Description>Dynamics CRM 2015 (7.0.X) Customer Workflow Template</Description>
    <DefaultName>CRM2015_7_0_X_WorkflowProject</DefaultName>
    
    <ProjectType>CSharp</ProjectType>
    <ProjectSubType></ProjectSubType>
    <SortOrder>1000</SortOrder>
    <CreateNewFolder>true</CreateNewFolder>    
    <ProvideDefaultName>true</ProvideDefaultName>
    <LocationField>Enabled</LocationField>
    <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
    <Icon>sw-file-icon.png</Icon>
    
    <!-- Indicates how many parent folders this item template should appear in -->
    <NumberOfParentCategoriesToRollUp>1</NumberOfParentCategoriesToRollUp>
  </TemplateData>
  <TemplateContent>
    <Project TargetFileName="CRM2015_7_0_X_WorkflowProject.csproj" File="CRM2015_7_0_X_WorkflowProject.csproj" ReplaceParameters="true">      
    </Project>
  </TemplateContent>
</VSTemplate>

Open ‘_preprocess.xml’ and change the 'TemplateInfo' element, this determines where your project template will be displayed along with the others when creating a new project. Here I want my templates to show up under C# and then a heading for my personal templates, followed by a level specifically for Dynamics CRM templates.

<?xml version="1.0" encoding="utf-8" ?>
<Preprocess>
  <!--
  You can specify the path where this should show up in the
  Add New Project / Add New Item dialog by setting the value below
  -->
  <TemplateInfo Path="CSharp\JLattimer\Dynamics CRM"/>
<Replacements Include="*.*" Exclude="*.vstemplate;*.jpg;*.png;*.ico;_preprocess.xml;_project.vstemplate.xml">
<add key="CRM2015_7_0_X_WorkflowProject" value="$safeprojectname$"/> </Replacements> </Preprocess>

Update 1/9/2015: Don't do this - this gets fixed in the step above now
Open the .csproj file for the Project Template Project in Notepad outside Visual studio. Replace these lines to make sure the namespace and assembly names match what the user enters when creating the project instead of whatever you decided to call your project.


<rootnamespace>$safeprojectname$</rootnamespace>
<assemblyname>$safeprojectname$</assemblyname>

Also open Assemblyinfo.cs under the ‘Properties’ folder in the project and make these replacements so they match the user input as well.

[assembly: AssemblyTitle("$safeprojectname$")]
[assembly: AssemblyProduct("$safeprojectname$")]

At this point if you have any other helper classes that you end up using you could go ahead and add them to the Project Template Project.
Right click on the Item Template Project and select Add -> Add Template Reference (SideWaffle project).

9

Choose the Project Template Project. It will add a .csproj file to the Item Template Project which will be flagged as if Visual Studio can’t find it. This is OK (at least until you try and check in to TFS anyway).

10

Creating a base workflow class template file

In the Item Template Project, add a folder called ‘ItemTemplates’, as you recall this wasn’t created originally when we installed the TemplateBuilder NuGet package. Under this folder create subfolders for each level you want represented in the hierarchy of file templates when creating a new file. I’ve opted for a similar structure to my project template. Right click on the last level and select Add -> New Item -> Extensibility -> SideWaffle -> SideWaffle Item Template and give it a name.

11

This will add a folder and several files to the project. In the ‘Definitions’ folder there will be four files created representing the top level of the new file dialog hierarchy. Since I only want this file to show up in the C# section I’m going to delete the VB and 2 Web related files. Now rename ‘CSharp.vstemplat-‘ to ‘CSharp.vstemplate’ adding the missing ‘e’ at the end of the filename. Delete the readme.txt file while you are at it. You’ll see an image file here as well, if you want update it based on the rules described earlier.

If you added 2 levels of folders under ‘ItemTemplates’ like I did you need also to update the ‘template-builder.props’ file under the Properties node in the project. Change the ‘ls-TemplateSubFolder’ element to match the bottom level folder.

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!--  
        This file was added by the SideWaffle NuGet Package http://nuget.org/packages/SideWaffle.
        You can specify SideWaffle settings in this file.
        
        After editing this file you may need to restart Visual Studio for the changes take effect.
  -->

  <!-- 
    You can override TemplateBuilder properties here.
   -->
  <PropertyGroup>
    <!-- This is the subfolder name where the templates will show up in Visual Studio. -->
    <ls-TemplateSubFolder>Dynamics CRM</ls-TemplateSubFolder>

    <!---
    You can set the following property to false to disable creating the template report
    -->
    <!--<ls-GenerateTemplateReport>false</ls-GenerateTemplateReport>-->
  </PropertyGroup>
</Project>

Add a new class file into the folder that was you lowest level in the hierarchy but above the ‘Definitions’ folder, this will be the workflow class file template.

Here is what you should have so far:

12
In ‘CSharp.vstemplate’ update the ‘DefaultName’, ‘Name’, and ‘Description’ elements. ‘DefaultName’ represents the default file name that gets generated when selecting the item. ‘Name’ shows in the middle of the new file dialog next to the image. ‘Description’ shows on the right side of the new file dialog to show extended information about the file.

Also replace the ‘ProjectItem’ element and substitute the name of the class file you just created. When deployed this will make sure that the name the user types in for a new filename replaces whatever you called the template file in this project.

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Item">
  <TemplateData>
    <ProjectType>CSharp</ProjectType>

    <DefaultName>WorkflowClass.cs</DefaultName>
    <Name>CRM Workflow Class</Name>
    <Description>Dynamics CRM Workflow Class</Description>
    <Icon> icon.png</Icon>
    <NumberOfParentCategoriesToRollUp>1</NumberOfParentCategoriesToRollUp>
  </TemplateData>
  <TemplateContent>
    <References />

    <ProjectItem TargetFileName="$fileinputname$.cs" ReplaceParameters="true">WorkflowClass1.cs</ProjectItem>

  </TemplateContent>
</VSTemplate>

Go find the original ‘Class1.cs’ file that was created in the Project Template Project and update this to whatever your ideal starting point would look like. Once you have everything set copy the contents to the class file in the ‘ItemTemplates’ folder in the Item Template Project and delete ‘Class1.cs’. The reasoning here is the project that deals with the project template has all the required references so it will be easy to see if there was a syntax error or a reference that was missed based on all the red unresolved symbol references. In the file template replace the namespace with $rootnamespace$ and replace the class name with $fileinputname$. This will make sure when the file is added it inherits the namespace of the existing project and the class name matches whatever the user entered for the file name.

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Workflow;
using System;
using System.Activities;

namespace $rootnamespace$
{
    public class $fileinputname$ : CodeActivity
    {
        private ITracingService _tracingService;
        private IOrganizationService _service;

        protected override void Execute(CodeActivityContext executionContext)
        {
            try
            {
                _tracingService = executionContext.GetExtension<ITracingService>();
                IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
                IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
                _service = serviceFactory.CreateOrganizationService(context.UserId);

                //Do stuff
            }
            catch (Exception ex)
            {
                _tracingService.Trace("Exception: {0}", ex.ToString());
            }
        }
    }
}

If you added any helper classes to the Project Template Project you could set up any replacement values (like the namespaces) if you are so inclined now.

With any luck your solution should build without errors. If so go ahead and F5 to starting debugging. This will launch a new ‘Experimental’ instance of Visual Studio for testing. When you do a File -> New -> Project you should see your new template.

13

On the new project Add -> New Item and look for your custom CRM workflow class to add to the project.

14

Build your project to download the SDK assemblies from NuGet to resolve all the missing references and now you're ready to dive in and tackle the meat of your task rather then wasting time on the tedious setup stuff.

If everything checks out and you are done testing you can use the VSIX file your solution created to install in your real instance of Visual Studio for everyday use or for distribution to others.

I should also point out that the 'Experimental' Visual Studio instance remembers which extensions were installed between sessions. That might cause issues when you end up recreating projects with the same name or start changing where things show up in the project dialog hierarchies. If this is the case you can reset the instance by running the 'Reset the Visual Studio 2013 Experimental Instance' shortcut located here:

C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio 2013\Microsoft Visual Studio SDK\Tools

If you are running Windows 8/8.1 you can search for 'reset' and you will probably see it come up in the search results. Running this will remove any of the previously installed extensions.

Hopefully this sparks some ideas on how you can use SideWaffle templates to speed up your day to day development. Besides plugins and workflows  creating project and item templates for HTML and JavaScript web resources or for unit testing scenarios also comes to mind. If you have any good ideas, post them in the comments!

You can download the code here:
https://github.com/jlattimer/CRM2015SideWaffleWorkflowTemplate