CRM 2013 – Plugins – Simple update plugin – Redeploying, improving and updating

I recently did a YouTube video which stepped through the process of creating a simple CRM 2013 plugin which ran on the update of the account entity.

The purpose of the video was to step through the process of creating a plugin and with that in mind I wanted to create the simpliest plugin I could think of.  Once you can do the process of creating a simple plugin and deploying it, it’s then a case of adding in your own logic and doing the process yourself.

In the first video – CRM 2013 – Create a simple plugin in CRM 2013 using the CRM Development Toolkit I explained some of the fundamental logic of a plugin (extending the IPlugin interface, the all important Execute method etc).  I also wanted to show how useful the CRM Developer toolkit was, particularly when it comes to creating and deploying plugins because it does a lot of the hard work for you.

I wanted to continue the journey of learning about plugins but without doing too much new code but I still wanted to add some new code so it’s interesting.

So I created a new video

CRM 2013 – simple update plugin, Redeploying, improving and updating 

This video updates the previous basic update plugin and this time I only trigger the plugin when a certain field Account Rating gets changed.  This is important thing to consider in update plugins, particularly in these days of autosaving.  Only trigger the plugin to run if certain fields are triggered, this will save server resources by not firing your plugin every 30 seconds.

It’s amazing the things you learn whilst writing blogs and recording videos.  I didn’t previously know how you deleted plugins using the CRM Development toolkit, so I started sniffing about and then worked out how to.   Below I run through changing the fields which trigger the account pre update plugin

Go to the CRM Explorer

You will notice the green plugins are the plugins in the solution you are in

nudge the down arrow and you can see the plugin steps

Update plugin 1

Edit the plugin step

Update plugin 2

The CRM Developer toolkit also allows you to delete the plugin or plugin steps, useful stuff

In the video – CRM 2013 – Simple update plugin – Redeploying, improving and updating I also run through a couple of important plugin concepts.

What’s passed into a plugin

The CRM 2013 SDK is a great document but a bit dry and difficult to get you started but once you are up and running there are lots of great explanations of how things work, this page is very useful for people writing plugins

Understand the Data Context Passed to a Plug-In

It explains what is actually passed into the plugin, the ServiceProvider has lots of goodies in like

IPluginExecutionContext

This has lots of data in like the user who triggered the plugin

IOrganizationService

Use this to access CRM and Create, Read, Update and Delete CRM records

context.InputParameters and context.OutPutParameters

The input parameters hold a collection and in that collect is the Target entity object, this has the values changed from the account update

there is also the tracing object

 

In the example I am going to use all three of these item and in most plugins you will use them but the page also has this table which explains what objects are passed into the context.InputParameters because it’s not always the Target object you should look for.  The picture below is a screenshot from the page

Update plugin 3

So what you should understand from the table above is, when a record or action triggers a plugin then usually either the entity is passed into the context.InputParameters or it’s an EntityReference.  You usually retrieve and cast this object out and then pass this to your plugin logic.

The one that used to catch me out was SetStateRequest passing the EntityMoniker.  What happened to me was I copied my boilerplate plugin code and I couldn’t understand why it wasn’t going into the code and running.  The reason was I was checking to see if the Target existed and if it did casting it to an entity and passing that into another method to do the work but it never went into my code.

So I had to debug the code to find there was no Target! finally I found the oddly named EntityMoniker and I worked out what to do from there.

 

 Plugin Stages

In the first plugin I couldn’t remember what the stages of a plugin were and their significance, all I could remember was they were linked to the database transaction and something about numbers going up in 10.

So I have done my homework and once again found some great information in the CRM 2013 SDK

event execution pipeline

The page has an interesting overview of the whole CRM architecture and it simplifies the stages
PRE-EVENT
PLATFORM CORE
POST-EVENT
The picture below is a screen from the Event Execution Pipeline page
Update plugin 4

As you know from registering plugins we can only register plugins

Pre-Validation – 10

Pre-Event – 20

Post-Event -40

The actually database transaction finishes in step 30 – Platform core operation and you can interfere with this, this is where the system plugins will run and kick off and updating system values and triggering other things.

An interesting point is the Pre-Validation, this is triggered before security checks have happened, so before CRM has checked the user can update the record maybe, basically before CRM checks the security roles and the PrincipalObjectAccess table (where all the security stuff is stored for users) is checked.

So that is the end of my quick plugin theory lesson and back onto the code, So along with the changing the plugin filter records (which you can see the RegisterFile.crmregister) I changed the code.

I now want to trigger the update on the change of AccountRating and I am going to read the value, which those of you who watched the video will know, OptionSetValue’s in CRM are held as ints.  CRM also holds Metadata on the OptionSet which holds the text.

My code is going to run based on the Int value because I think maybe in the future people might change the text in the OptionSet from Gold, Silver, Bronze to 1, 2, 3 or Great, OK, Rubbish.

I also get the guid of the user (entity SystemUser)who triggered the plugin by updating the AccountRating field.  This value is passed in with the plugin context and can be retrieved

Guid userId = context.InitiatingUserId;

There are two different user guid passed into the context and you should know which is which

context.initiatinguserid: the systemuser GUID who actually triggered the plugin
context.userid : gets the impersonated systemuser GUID

In my case I want the user who triggered the plugin

I then lookup the fullname and domainname of the user because this is more useful than a guid to end users but the guid is great for retrieving more data about that record.  I use the IOrganisationService to retrieve more fields from the record.  To do this all I need is the entity logical name, guid and the fields I want to retrieve

Entity userName = service.Retrieve(“systemuser”, userId, new Microsoft.Xrm.Sdk.Query.ColumnSet(new[] { “domainname”, “fullname” }));

Then it’s a case of concatenting them into string and adding this to the account entity being updated.

The full code is below

 

// <copyright file="PreAccountUpdate.cs" company="Microsoft">
// Copyright (c) 2014 All Rights Reserved
// </copyright>
// <author>Microsoft</author>
// <date>4/3/2014 12:04:45 AM</date>
// <summary>Implements the PreAccountUpdate Plugin.</summary>
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.1
// </auto-generated>
namespace HoskCRMDev2013.Plugins
{
using System;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;

/// <summary>
/// PreAccountUpdate Plugin.
/// Fires when the following attributes are updated:
/// All Attributes
/// </summary>
public class PreAccountUpdate: Plugin
{
/// <summary>
/// Initializes a new instance of the <see cref="PreAccountUpdate"/> class.
/// </summary>
public PreAccountUpdate()
: base(typeof(PreAccountUpdate))
{
base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Update", "account", new Action<LocalPluginContext>(ExecutePreAccountUpdate)));

// Note : you can register for more events here if this plugin is not specific to an individual entity and message combination.
// You may also need to update your RegisterFile.crmregister plug-in registration file to reflect any change.
}

/// <summary>
/// Executes the plug-in.
/// </summary>
/// <param name="localContext">The <see cref="LocalPluginContext"/> which contains the
/// <see cref="IPluginExecutionContext"/>,
/// <see cref="IOrganizationService"/>
/// and <see cref="ITracingService"/>
/// </param>
/// <remarks>
/// For improved performance, Microsoft Dynamics CRM caches plug-in instances.
/// The plug-in’s Execute method should be written to be stateless as the constructor
/// is not called for every invocation of the plug-in. Also, multiple system threads
/// could execute the plug-in at the same time. All per invocation state information
/// is stored in the context. This means that you should not use global variables in plug-ins.
/// </remarks>
protected void ExecutePreAccountUpdate(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}

// TODO: Implement your custom Plug-in business logic.
// Obtain the execution context from the service provider.
IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;

// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parmameters.
Entity entity = (Entity)context.InputParameters["Target"];
string entityDescription = "HOSK CRM DEV FIRST PLUGIN HAS RUN AND UPDATED THE DESCRIPTION";

//accountratingcode
if (entity.LogicalName == "account")
{

try
{
if (entity.Attributes.Contains("accountratingcode"))
{
Guid userId = context.InitiatingUserId;
Entity userName = service.Retrieve("systemuser", userId, new Microsoft.Xrm.Sdk.Query.ColumnSet(new[] { "domainname", "fullname" }));
string fullname = (string)userName.Attributes["fullname"];
string domainName = (string)userName.Attributes["domainname"];
entityDescription = "user " + fullname + "has the domain name of " + domainName;
entityDescription = entityDescription + Environment.NewLine + DateTime.UtcNow.ToString();
OptionSetValue accountRatingCode = (OptionSetValue)entity.Attributes["accountratingcode"];
if (accountRatingCode.Value == 1)
{
entityDescription = entityDescription + Environment.NewLine + "account rating = gold";
}
else if (accountRatingCode.Value == 100000000)
{
entityDescription = entityDescription + Environment.NewLine + "account rating = silver";
} else {
entityDescription = entityDescription + Environment.NewLine + "account rating = bronze";
}

if (entity.Attributes.Contains("description"))
{
//string entityDescription = (string)entity.Attributes["description"];
//entityDescription = "HOSK CRM DEV FIRST PLUGIN HAS RUN AND UPDATED THE DESCRIPTION";
entity.Attributes["description"] = entityDescription;
}
else
{
//entityDescription = "HOSK CRM DEV FIRST PLUGIN HAS RUN AND UPDATED THE DESCRIPTION";
entity.Attributes.Add("description", entityDescription);
}

}
}

catch (FaultException ex)
{
throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
}
}
}
}
}
}

 

6 thoughts on “CRM 2013 – Plugins – Simple update plugin – Redeploying, improving and updating

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.