CRM 2016 – Maximum plugin size

Don’t watch the clock; do what it does. Keep going. Sam Levenson

Consistency is what separates the amateurs from the experts #HoskWisdom

Microsoft Dynamics CRM plugins have a maximum plugin size,  if you exceed this, the plugins will no longer import.

The limit is more likely to be hit in a CRM online projects because sandbox plugins can only include 1 dll, where CRM OnPremise projects can reference plugins in the GAC.

You can put common libraries in the GAC on the server.  I have worked in a project where we did this but it meant we had to install dll‘s onto multiple servers due to load balancing.  It worked but I’m not sure I liked this method and the majority of projects are being delivered with Microsoft Dynamics CRM online.

What is the plugin size?

I searched for the limit and it was difficult to find but I finally found this forum post

What is the maximum file size of plugin/workflow library in CRM?

The limit of the plugin is 8 Megabytes, this is a .NET limit on the httpRunTime MaxRequestLength setting held in the web.config.

Microsoft Dynamics CRM on premise projects can increase this value and I have had to do this before to import large solutions and reports

Why is this a problem?

The problem occurs when your solution is larger than 8 megabytes, you will get a timeout error.  For CRM online projects because you can’t increase  the setting without access to the web.config.

Early bound code can bloat plugins if you generate the early bound model for all entities and not just the ones you need.

Good practice is to limit the entities you create the early bound model for and you can do this with Early Bound Generator, available in the XRMToolbox

It doesn’t only affect CRM Online because the limit can happen for sandbox plugins in OnPremise deployments, highlighted in this article Import CRM Dynamics 2015 Managed Solution – Exceeded Content Size

What can you do

There are few options to avoid this

  • Limit the early bound classes to the entities referenced in the plugin
  • split your plugin into a separate solutions
  • don’t use global business logic classes, only include the business logic a group of plugin needs.

This might not be a problem but it should be something to be aware of, if your solutions are growing in size you will need to organise them into smaller solutions.

I talk about solutions organisation in this article CRM 2016 – What’s the best way to organise solutions in Microsoft Dynamics CRM

Advertisement

CRM 2015 – Why is the System user shown as disabled in the plugin registration tool

Someone asked me why the System user had the word disabled in brackets in the plugin registration tool when editing plugins and can we I still use it.

I have set plugins to run as SYSTEM but I couldn’t remember if the SYSTEM user had disabled in it. I fired up the plugin registration tool, selected a plugin, clicked the Run in User’s Context and found this

System User Disabled

I wasn’t sure whether I should be worried about the disabled or not, had it always been there?

In this example the CRM developer was setting a plugin to run as the SYSTEM user because the plugin need to run with more security privildges than the calling user.

To learn about Impersonation and plugins read my blog post

CRM 2015 – Understanding impersonation in plugins and knowing when to use it

What is the SYSTEM user

My knowledge of the SYSTEM user is it’s a super super in built user which you cannot see inside CRM in any of the views, the SYSTEM user won’t appear in disabled or enable user views inside CRM.

SYSTEM user is used by plugins, workflows to run with elevated privileges and when the user updates any records you will see SYSTEM.

Gonzalo Ruiz has written a great blog post on the SYSTEM and INTEGRATION

Explaining the built-in SYSTEM and INTEGRATION users

The blog post has a few interesting points to consider

  • Nobody can log in CRM as either of these 2 users.
  • These users are always on the root business unit.
  • No security applies to these users (any action is allowed when executing as SYSTEM or INTEGRATION), all security validations are bypassed.

I’m guessing the SYSTEM user shows this SYSTEM (disabled) to indicate the user is disabled from user CRM as a general user.

Why is named SYSTEM (disabled)?

SYSTEM (disabled) lets the CRM developer know they are selecting the special SYSTEM user and not a user created user who could also have the name SYSTEM.

I did check some other CRM instances (CRM 2011, CRM 2013 and CRM 2015) to see if SYSTEM always had SYSTEM (disabled) and in the three instances I looked at all had the words disabled in brackets after the SYSTEM user.

I’m not sure labelling the system user as disabled is the best idea because it caused concern we had somehow disabled the SYSTEM user or maybe we shouldn’t select it because it was disabled.

It seems it’s just Microsoft Dynamics CRM having a quirky way to indicate the SYSTEM user is special and not to be confused with other non super users (surely there is a better way)

CRM 2015 – How to diagnose plugin errors

failure-to-diagnose

This blog talks about a plugin error which I have seen a number of times when using the Plugin Developer Toolkit, it will discuss how to diagnose plugin errors in general.

Before I talk about plugins can I encourage people to vote on the connect item to get the CRM Developer toolkit working with Visual studio 2013, if you can’t wait for Microsoft to fix it then read my blog post CRM Developer Toolkit Alternatives

Hosk CRM Brain

I like to think of my blog as my CRM brain uploaded to the internet, so whilst reading this you are currently crawling through my CRM brain.  This allows me to search my CRM brain when my real brain forgets how I resolved a problem, actually sometimes I even forget I have experienced a problem and get pleasantly suprised when I have found not only did I have the problem before but I blogged the solution.

The benefit of me blogging about CRM is I benefit and other people who read and search my blog.

My goal in writing articles focused on errors is not only to write about how to resolve the issue but additionally look into the cause of the problem and , the WHY.

Knowledge helps when things go wrong

There is always a difference between theoretical knowledge and practical knowledge.   You can learn the theory of something but find it’s different when you try to use the theory in practical use (e.g. after reading about plugins you then try to write a plugin) you there are gaps in knowledge which you quickly find.

When developers start to plugins they usually go through

  • learn to write plugin code, find lots of errors
  • try to deploy plugin, error haven’t signed the plugin, error permissions etc.
  • Slowly but surely the developers experience fewer problems or problems they know how to resolve

Indepth knowledge of how Microsoft Dynamics CRM works and the underlying plugin infrastructure becomes extremely important when things go wrong.  When errors appear and hold up development, developers need to understand the cause of the error but why it’s complaining, the answer is usually understandable if you understand

  • how the CRM developer toolkit works
  • The various parts of the CRM Developer toolkit
  • The CRM plugin execution framework
  • The roles and privileges needed to deploy plugins

Some useful Hosk Plugin blog posts

The CRM developer toolkit is great (which is why I get annoyed it hasn’t been updated because it automates a lot of the repetitive actions needed to deploy a plugin using the plugin registration tool.) but I feel CRM Developers should learn how to use and deploy plugins using the plugin registration tool.

I use the plugin registration tool to inspect what plugins and steps are deployed and the ability to change the view to see what plugins are deployed for each entity is really useful when investigating bugs on CRM solutions you are not familiar with.

Most of the time you don’t want to mix using the plugin registration tool and the CRM developer toolkit because the CRM developer toolkit will overwrite the changes you make manually next a developer uses it to deploy CRM customizations.

The plugin registration tool is great for viewing the plugins deployed and its portability allows you to use it in customer environments.

If the CRM Developer toolkit gets in a mess you might need to use the plugin registration tool to quickly update or deploy a plugin whilst you fix the plugin developer toolkit.

I had an error previously but the problem was the CRM developer toolkit had got out of sync and I struggled to resolve this problem.  You can read about my frustrations with the CRM Developer toolkit

Dealing with Plugin errors

If When you experience a plugin error I would recommend you first read my blog on common problems because I cover the most common errors.

If you don’t find the answer then stop and think about the potential cause of the problem.  Many developers can go into a mild panic mode when they encounter an error, instead of logically thinking about the problem they instantly go and get a senior developer to help them.

Then when the senior developer is at their desk, they explain the problem.  The process of explaining the problem to the developer, the solution to the problem can become clear.  This is known as rubber ducking or I call this Cardboard developer

When you encounter a plugin problem or CRM developer problems, follow these steps

  1. Stop
  2. Engage Brain\Think
  3. What’s happening?
  4. What should happen?
  5. Make a list of the possible causes of the problem
  6. Investigate your list

If you can’t resolve the problem, you can then at tell the developer what you know, what you have tried.

I don’t encourage any developers to suffer in silence but it’s more beneficial for you own personal learning if you try to understand and resolve problems yourself.  The major benefit of trying to resolve the problem yourself is you get in the habit and become less dependent on the help of your colleagues.

Don’t Assume, know

Don’t Assume, Know is a Hosk mantra I tell myself when investigating problems or debugging.  I have wasted many hours investigating problems and looking for solutions based on an incorrect assumption.  When dealing with problems don’t assume anything, check assumptions and cross them off.  Lots of times you will find the problem.

Plugin Error Messages

The error messages Microsoft Dynamics CRM throws are a mixture of a confusing statement with a nugget of truth tucked inside.  To developers new to CRM development they are just unhelpful messages.

As your experience and knowledge of CRM development increases you will find they often point you in the right direction but you need to have built up a map of the CRM landscape, so you know where to go and check.

Plugin Error Example

This is common error I have experienced a few times but I was trying to deploy a plugin using the CRM developer toolkit and I got this error

Error registering plugins and/or workflows. Plug-in assembly does not contain the required types or assembly content cannot be updated.

Lets break down the message to try and decify the problem

  1. It can’t register the plugin/workflow
  2. Plugin Assembly does not contain required types or the assembly cannot be updated.
  3. So we know the plugin assembly (the DLL) exists but it cannot update it.

The first thing to do is know not assume.  So I opened the Plugin Registration tool and found the DLL.  I could see it had three steps.

I then looked at the RegisterFile.crmregister file and found this had two steps.

The problem was because we were trying to update an assembly with 2 steps but the assembly had 3 steps.  It couldn’t update the assembly because it was too different and this error message was letting us know (in a slightly confusing way).

I have experienced this problem before and the solution to the problem is to unregister the Assembly and install it again.

This problem has occurred when I have created new plugins and sometimes when I have updated a plugin maybe in a different solution but for some reason I couldn’t update the DLL.

In this case how the extra step got into the assembly was a complete mystery but it’s OK to delete the plugin assembly because I knew I was going to deploy it again and install a new version of the Plugin assembly.

CRM 2015 – How to decompile a plugin Assembly (DLL)

This blog post looks at how to decompile plugin code because I had a problem recently where I needed to do it.  The process will work in all version of CRM because fundamentally it works at DLL level rather than CRM level.

It’s a useful to understand because there are some instances where you need to know what code has been deployed because it might be different from the code you have or the code in your source control.

The problem – Why isn’t my fix working?

A bug fix I had deployed and tested in our dev environment and test environment suddenly stopped working when deployed to the customer environment.

This isn’t an unusual problem but an annoying one for developers.

Don’t assume, know

When bug fixing and testing try to avoid the trap of assuming because assumptions are often wrong, resulting in missing the obvious source of the problem, spending time testing other possible reasons for a problem.

Usually when a bug fails on a different environment I want to recreate it in the DEV environment where I can easily debug it.
For this problem we have two internal instances

DEV
TEST

I tested my fix in DEV and it seemed to work and then I tested the fix in internal TEST environment and found my fix didn’t work, why?

Finding the plugin code deployed

There are instances when you need to find the code deployed in a plugin. It’s possible to version plugins but CRM developers don’t bother versioning their plugins and I currently don’t version the CRM plugins.

Plugins can be versioned in the same way solutions are version in CRM

major.minor.build.revision

You need to think about versions and solutions because when you change the major number of a plugin assembly CRM considers it a completely different plugin assembly

Microsoft has a good article on Plugin Assembly

This article on how to upgrade version

Hardcore CRM: Do a major upgrade of a plugin version!

What to do if you don’t use assembly versioning

The problem I had was how could I tell what code was inside the plugin and if my fix was deployed?  The code in TFS (Source Control) was correct and up-to-date but I tested the functionality and could see it wasn’t working.

I decided to download the plugin assembly dll and decompile the code.

The tools I used was

XRMToolbox

Go to releases and click latest release if you want to download the application not the source
https://github.com/MscrmTools/XrmToolBox/releases

The XRMToolbox has a tool called Assembly Recovery Tool. This tool allows you to connect to a CRM instance, it loads all the assembly DLL’s (which can some time), you can choose to download any of the Plugin assemblys(dll file). This will give you the dll, which you can decompile using a different tool.

Once you have the DLL file you can decompile the dll using a tool such as

ILSPY

There are lots of other decompilers out and the main reason I use ILSPY is because its free

To use the decompiler you open it up, select a DLL or copy one to the application, below I have selected a HoskCRMDev2013 plugin.  The dll’s in blue are dependant plugins

decompile plugin

If you then click on one of the methods, it decompiles the code, which you can view it in the yellow right hand screen.

decompile plugin1

ILSPY has a few neat options to save the to file.

A few notes on decompiling DLLS

DONT decompile DLL’S you or the company you work for doesn’t own because basically you are viewing the source code.

I’m not sure about the legality of it but it’s probably illegal, particularly if you don’t own the intellectual property.

If you aren’t sure if you should decompile a DLL think about it like this.  If you went to the owner of the source code and asked to view the source code, would they let you?

A common cause of decompiling plugins/custom workflows is to see what has been deployed if plugin versioning is not used.

Be warned decompiled code is slightly different, often the variables don’t have the same name.

If you are concerned about your dlls being decompiled there is a tool to encrypt them, I haven’t used it.  The discussion on StackOverflow is a good place to start if you are interested

Protect .NET code from reverse engineering?

When might you need to decompile a DLL?

Surprisingly there are quite a few scenarios where you might need to decompile a DLL, I will go through some of the common scenarios but the

Lost source control

I have seen scenarios where a company has lost the source control for code which has been deployed.  Often its old legacy systems and most people who worked on the project have left the company

Deployed with source control checked out pending

A developer deploys some code, forgets to check in the code.  The developer then might leave the company, go on holiday and can’t be contacted.  You need to know what code has been deployed and what code might not be checked it.

Plugin won’t deploy from CRM Developer toolkit

Sometimes a plugin won’t deploy and you will get an error like this

Error registering plugins and/or workflows. Plug-in assembly does not contain the required types or assembly content cannot be updated

The reason for this error is often because the deployed assembly file (DLL) is different.  It might have an extra step and you are not updating the file but trying to replace it.  CRM doesn’t like this.

I had an example of this recently and there was an additional step in the deploy DLL.  I used the plugin registration tool to compare the steps but I wasn’t sure what was in the extra step.  So I download the DLL and looked at the code.

What has been deployed

This is the problem I was experiencing for this bug.  I needed to check if the code deployed was the same as the code in source control, the only way I could do it was to decompile the code and compare.

Plugins without versioning

A common cause of decompiling plugins/custom workflows is to see what has been deployed if plugin versioning is not used.

Multiple Developers Versus Source Control

When you have multiple developers working on a project, source control can sometimes lead to problems with people not getting the latest code and deploying an older version

Who deployed the different plugin?

I was wondering how the different plugin version got deployed. I wondered if I could work out what solutions were imported

I remember review a useful tool a while ago the Solution History tool, which is a tool which works with the XRMToolbox

You can download the tool here

The tool usefully told me what solutions had been imported and what files were included.

I could see the solution had been changed and who imported but I couldn’t tell what version of the Plugin Assembly had been deployed.

This tool would be extremely useful if the project you are working on imports multiple solutions without changing the version number.

What gets updated when you import a plugin to the database?

In the background when you import a plugin it updates system entity called plugin. I wondered if I could select these entities to see when they had been updated.

The problem is you can’t select system entities in an advanced find, so I remember another tool which works with the XRMToolBox the FetchXML Builder

You can get the tool here

This tool is useful because it has GUI interface to allow you to create Fetch XML queries. This means you can select system records.

The real beauty of this is you can create FetchXML queries and then use them in your plugins.

I wanted to use the tool to easily query the system entity of plugin

retrieve plugin

Which produces the FetchXML below

<fetch count ="50 " >
<entity name ="pluginassembly " >
< filter>
< condition attribute= "name" operator=" eq" value ="Full Assembly Name " />
</ filter>
</entity>
</fetch>

This allowed me to retrieve when the plugin was changed and by who. I was wondering if someone had directly deployed a plugin to the Test environment and bypassing the Solution import phase but it turned out not to be the case.

I couldn’t work out who had deployed an old plugin version and the important point was I had the resolution to the problem (although you would like to be confident it wouldn’t happen in the future)

Summary

This blog should show you how to download and decompile a DLL file.  There are certain scenarios where you aren’t sure if the code deployed is the same as the code in your source control.

Downloading and decompiling DLL’s is another tool to put in your development bag, make sure you use it for good not evil.

CRM 2013 – Workflow error AccessCheckEx

I was investigating a bug on CRM 2013 , I got the exception below

Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: SecLib::AccessCheckEx failed. Returned hr = -2147187962, ObjectID: f115e97d-8e19-e511-80ca-000c292122be, OwnerId: 89ed0bdd-7ecd-e411-80c7-000c292122be,  OwnerIdType: 9 and CallingUser: 2ed69167-0bcf-e411-80c7-000c292122be. ObjectTypeCode: 4200, objectBusinessUnitId: 5f964320-05f4-e411-80c9-000c292122be, AccessRights: WriteAccess Detail:
<OrganizationServiceFault xmlns:i=”http://www.w3.org/2001/XMLSchema-instance&#8221; xmlns=”http://schemas.microsoft.com/xrm/2011/Contracts”&gt;
  <ErrorCode>-2147220891</ErrorCode>
  <ErrorDetails xmlns:d2p1=”http://schemas.datacontract.org/2004/07/System.Collections.Generic”&gt;
    <KeyValuePairOfstringanyType>
      <d2p1:key>OperationStatus</d2p1:key>
      <d2p1:value xmlns:d4p1=”http://www.w3.org/2001/XMLSchema&#8221; i:type=”d4p1:string”>0</d2p1:value>
    </KeyValuePairOfstringanyType>
    <KeyValuePairOfstringanyType>
      <d2p1:key>SubErrorCode</d2p1:key>
      <d2p1:value xmlns:d4p1=”http://www.w3.org/2001/XMLSchema&#8221; i:type=”d4p1:string”>-2146233088</d2p1:value>
    </KeyValuePairOfstringanyType>
  </ErrorDetails>
  <Message>SecLib::AccessCheckEx failed. Returned hr = -2147187962, ObjectID: f115e97d-8e19-e511-80ca-000c292122be, OwnerId: 89ed0bdd-7ecd-e411-80c7-000c292122be,  OwnerIdType: 9 and CallingUser: 2ed69167-0bcf-e411-80c7-000c292122be. ObjectTypeCode: 4200, objectBusinessUnitId: 5f964320-05f4-e411-80c9-000c292122be, AccessRights: WriteAccess </Message>
  <Timestamp>2015-06-23T10:02:24.458209Z</Timestamp>
  <InnerFault i:nil=”true” />
  <TraceText>

[Microsoft.Crm.ObjectModel: Microsoft.Crm.ObjectModel.SyncWorkflowExecutionPlugin]
[0dac4467-fb18-e511-80ca-000c292122be: ]
Starting sync workflow ‘Task-Workflow’, Id: 04ac4467-fb18-e511-80ca-000c292122be
Entering ConditionStep1_step: If  Process contains data and is active
Entering SetStateStep4_step: Change record status to completed
Sync workflow ‘Task-Complete’ terminated with error ‘SecLib::AccessCheckEx failed. Returned hr = -2147187962, ObjectID: f115e97d-8e19-e511-80ca-000c292122be, OwnerId: 89ed0bdd-7ecd-e411-80c7-000c292122be,  OwnerIdType: 9 and CallingUser: 2ed69167-0bcf-e411-80c7-000c292122be. ObjectTypeCode: 4200, objectBusinessUnitId: 5f964320-05f4-e411-80c9-000c292122be, AccessRights: WriteAccess ‘
</TraceText>
</OrganizationServiceFault>

Initial thoughts on the Error

I got this error and there were a few things I found interesting

This error was surprisingly informative

The error was thrown by a non code workflow but the cause of the error was thrown by a GUI workflow which was being triggered when a workflow tried to assign an activity.

I was impressed by the level of logging which was generated by a GUI/non code workflow.

I had never thought about it but this line

Microsoft.Crm.ObjectModel: Microsoft.Crm.ObjectModel.SyncWorkflowExecutionPlugin

indicates CRM runs the GUI workflows using code, which must translate the actions into code. This is obvious but I hadn’t thought about it, until seeing the error.

You can use the callerid to find what user is doing the update and check what security roles the user has.

EntityTypeCode

In the error message you can see it mentions ObjectTypeCode

CRM 2011/2013 – Javascript to get the object type code of an entity

Each entity has an individual typecode, this CRM SDK page shows you the values of the default entities

Entity Type Code 4200 is ActivityPointer, which is interesting because the problem was being caused by an update to a task record.

Clues

AccessCheckEx failed – AccessCheckEx is something to do with security and access

In the error message you can see

AccessRights: WriteAccess

This is clearly telling us the user doesn’t have Write access, e.g. the user isn’t allow to update a certain

What was the cause

This bug was partly caused by the complexity of the CRM solution and the different customizations.

Solution complexity refer to not only the customizations which exist in the solution but the number of different customizations.  When a CRM solution has lots of different customizations e.g. workflows, plugins, business rules being triggered at the same time it makes it difficult to understand what is changing a value.

Below is what was happening

  1. A task was updated then saved
  2. This triggered a pre plugin on the task entity
  3. The plugin assigns the case record
  4. A plugin was triggered on assign of the case, which assigned all the open tasks to the new case owner
  5. The plugin(s) finished
  6. A workflow was triggered, which tried set the task to complete.

The error was thrown because the workflow was trying to update the task but the user only had privileges to update tasks they owned.

The reason this bug suddenly appeared was because the assign plugin was added and it wasn’t picked up in DEV testing because developers tested the code using users with System Admin privileges, which I have talked about before

The System Administrator role is a benefit and a curse to CRM developers

It’s tricky to test the effect of adding plugins

Having lots of different types of customizations adds to the complexity of your CRM solution, complex solutions are difficult to debug, understand and extend.

The Solution

Usually with bugs where the user doesn’t have the right security privilege the easy answer is to give the user role those security privileges.

For this bug it wasn’t the correct solution because the users only had access to tasks they owned and we didn’t want to suddenly give them permissions to update tasks they didn’t own.

The plugin code was running in a PRE plugin, so I couldn’t move the task completing code into this plugin.

The bug was becoming more tricky because I did want to keep the case assigning code in their but I didn’t want the assigning case plugin to run and assign the task to the new case owner because the task was about to completed.

My solution was to stop the assign plugin being triggered if was called by another plugin

Read how to do that in the blog below

CRM Plugins – Stopping infinite loops and understanding PluginExecutionContext.Depth

I then created a post task plugin to complete the task.  I didn’t need to do this but it seemed it would be easy to understand if all the changes were made by plugins.

There was an unsuccessful fix when I used impersonation to close the task as System Admin but the users didn’t like the tasks being closed by System Admin, they wanted the user who updated the task to complete the task.

You can read about Impersonation in plugins in the blog post below

CRM 2015 – Understanding impersonation in plugins and knowing when to use it

CRM 2015 – Understanding impersonation in plugins and knowing when to use it

Plugins are usually set to run as the calling user but sometimes you need to run a plugin with System User privileges.

Before I explain how, we should first look at the why and how you impersonate users inside a plugin

I have been writing a bit about plugins recently

For some step by step guides to creating plugins check out the CRM Code examples section on the page Hosk’s CRM Developer Articles, I created some Youtube videos CRM 2013 Plugins

What is impersonation

When you create a plugin in CRM (step by step blog post here) you write an execute method which gets passed in

IServiceProvider serviceProvider

This has lots of variables and objects such as

  • IPluginExecutionContext
  • ITracingService
  • IOrganizationServiceFactory

You use the IOrganisationServiceFactory to create a IOrganizationService.  The IOrganizationService is the CRM SDK, it provides you access to CRM programmatically .  CRM developers use the IOrganizationService (which I will often call CRMService) to

  • CRUD operation – Create, Retrieve, Update, Delete records in CRM
  • Assign records
  • Change status
  • pretty much everything you can do in CRM

Here is the code to create the IOrganizationService, taken from the Plugin class of the CRM Dev toolkit

// Obtain the Organization Service factory service from the service provider
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

// Use the factory to generate the Organization Service.
this.OrganizationService = factory.CreateOrganizationService(this.PluginExecutionContext.UserId);

When creating your plugin you will notice there is a Run in Context setting, most of the time you will choose to Run In Context of Calling User.

Create Account Plugin 1

99 percent of the time Calling User is the right choice

It’s usually the right choice to use the Calling User because any updates, retrieves or any interaction with CRM data will be done using the calling users identity and privileges.

If you visualize a plugin as an automated extension of the CRM form, it’s likely you want the the code in the plugin to run with security privileges as the calling user.  It allows you not to abdicate the need to apply security to the plugin code and pass this off the users security profile and CRM.

The PluginExecutionContext (which I explain in this blog post) has the field called UserId.  We pass this to the IOrganizationFactory to create an IOrganizationService with the user who initiated the plugin.  The good news is the CRM Dev toolkit does all this for us, so we can just chill out and get the IOrganizationService and work on the business logic.

// Use the factory to generate the Organization Service.
this.OrganizationService = factory.CreateOrganizationService(this.PluginExecutionContext.UserId);

Running the CrmService (IOrganizationService) as the calling user means you are interacting with the data in CRM as the calling user, which means

  • The IOrganizationService can only retrieve data the user can retrieve
  • When IOrganizationService updates, creates records they are stamped with the calling user
  • The plugin cannot do anything the calling user cannot do

The calling user setting adheres to the security role assigned to the calling user, integrity of your precious CRM data is kept intact.

Why impersonate System User

If running plugins as the calling user is so good, why impersonate other users or system admins.

  • What if you need to retrieve records the user doesn’t have access to?
  • What if you need to update records the user doesn’t have access to?

You might be thinking, “if the user doesn’t have access to those records, maybe the plugin shouldn’t be updating them”.

It’s a point to consider point but sometimes you want to create records or update records based on the action/status of an entity to move the code to the next stage/state.

Sometimes you want to assign a record to another user when a record goes to a certain state but you wouldn’t want users to be able to assign records.

How is running in context different from impersonation

The running in context setting on a plugin mentioned early runs the whole plugin in that context.

Impersonation allows you to run a small section of code in another context.

Impersonation gives the CRM developer more flexibility to target a particular action in a plugin they would like to run with elevation permissions.  The rest of the plugin actions can be run as

Dangers of impersonation

The downsides of impersonation is it potentially gives users running the plugin\custom workflow enhanced security privileges which could lead who we don’t want updating\deleting records doing exactly that.

When using impersonation in plugins consider accountability and audit trails.  When a plugin impersonates a system user or a different user, the impersonated user will update those records.  It can be confusing/worrying for users when looking at audit records to see System Admin updating records.

I’m sure there have been thousands of support calls querying why System Admin has been updating records.

Impersonation Code

Easy way to impersonate System User is to pass null to the CreateOrganizationService and it will run as System user, find out more in this CRM SDK article

// Use the factory to generate the Organization Service.
OrganizationServiceImpersonated = factory.CreateOrganizationService(null);

You can pass in a different user Id to the CreateOrganizationService to create an IOrganizationService

IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(context.InitiatingUserId);

The sample code in the CRM SDK article impersonates using OrganizationServiceContext.  I personally have used this

Sample: Impersonate using the ActOnBehalfOf privilege

// Retrieve the system user ID of the user to impersonate.
OrganizationServiceContext orgContext = new OrganizationServiceContext(_serviceProxy);
_userId = (from user in orgContext.CreateQuery&lt;SystemUser&gt;()
where user.FullName == "Kevin Cook"
select user.SystemUserId.Value).FirstOrDefault();

// To impersonate another user, set the OrganizationServiceProxy.CallerId
// property to the ID of the other user.
_serviceProxy.CallerId = _userId;

Impersonating during plugin-in registration

You can impersonate a user during plugin-registration but I’m not entirely sure why you would do this, so I’m not going to talk about.  Most CRM developer will impersonate someone inside a plugin.

If you want to learn more read

Impersonate another user

and the articles in the futher reading section

Why, What, Where, When

What is Impersonation

Impersonation in plugins/custom workflows is creating an IOrganisationService as a different user

Why use Impersonation

Use impersonation when you need to update/retrieve/delete/Create records users security roles doesn’t let them.

Where

If you don’t want to increase users security roles but still want the plugin to do a particularly action.  e.g. delete a record

When

You use impersonation in plugins/customer workflows

Further reading

Scott Durow has an excellent article on Impersonation

User Impersonation in Plugins, Workflow and Dialogs

CRM SDK 2015 impersonating User in plugins

Dave Berry does a good job explaining Impersonation – CRM Impersonation Explained

Awesome Pixar impersonation picture from here

FetchXML reports do not trigger RetrieveMultiple plugins in CRM 2011

 

It is by going down into the abyss that we recover the treasures of life. Where you stumble, there lies your treasure.
Joseph Campbell

The CRM forums are a great resource for learning interesting solutions and problems in Microsoft Dynamics CRM

I read this interesting gotcha/CRM Quirk this week about a CRM developer who had written a plugin which was triggered on the RetrieveMultiple event on his custom entity.  The user was wondering why his plugin was not being triggered when the user ran a report using FetchXML.

RetrieveMultiple Plugins, What, Why and How?

So you can have plugins which trigger on RetrieveMultiple for an entity.  Some readers are thinking why would you want to do that?

Reasons like

  • Translate data held in the CRM database
  • Exclude data from RetrieveMultiple queries
  • Log RetrieveMultiple requests
  • Calculate some fields in the retrieve from other entities
  • Decrypting encrypted fields

There are lots of other reasons, but they give you an idea.  The user wouldn’t even know a plugin had run because they would just receive the results as normal.

The retrieve multiple is triggered in CRM when the user

  • Runs an advanced find
  • User clicks on a view (e.g. active accounts)
  • Quick search
  • Associated views
  • Plugins doing retrieve multiples
  • Console apps doing retrieve multiples

 

What about reports and FetchXML

So why don’t reports and FetchXML trigger the RetrieveMultiple plugin

The answer was explained in this forum post answer by CRM legend David Jennaway who is a CRM MVP, when I was starting in CRM development David had a fantastic CRM blog which I followed avidly and still do, but but unfortunately for the CRM community David rarely writes any blog posts these days (2 post in 2014).

Reports do not trigger plugins. SQL reports bypass CRM completely, and Fetch reports also bypass the plugin pipeline
 He also add this answer
Just to clarify the last post, in CRM 2011 fetch queries are submitted via the RetrieveMultiple message, whereas in CRM 4 they were submitted via the Fetch message and (bizarrely) you could handle them in plugins on the Execute message
This logically makes sense because SQL reports definitely would hit the plugin pipeline and I’m guessing FetchXML when run from reports must be run using a slightly different mechanism (I have no knowledge of this)
I’m not sure if this is the same in CRM 2013 but I’m guessing it’s probably going to be the same because I don’t think there have been changes in those areas of functionality.

CRM 2011/2013 – Common Plugin Errors and Isolation Mode

I got this plugin error whilst trying to deploy my custom workflow

Error registering plugins and/or workflows. The resource string “ErrorSerializingRegFile” for the “RegisterPlugin” task cannot be found

I have had this error many times, so I thought I would go through the common plugin errors

 

Common plugin errors

Below is a list of the common plugin errors which can frustrate you for a while

 

Forgetting to sign the plugin

This is a common thing to forget.  If you plugin won’t register you need to sign the plugin, you can see steps how to do this on my blog post

CRM 2013 – Step by Step Update Plugin Tutorial using the CRM 2013 Development Toolkit

Solution – sign the plugin

 

Haven’t checked out the crmregister file

This error occurs if the crmregister file is set to read only

https://crmbusiness.wordpress.com/2014/01/22/crm-2011-error-registering-plugins-andor-workflows-the-resource-string-errorserializingregfile-for-the-registerplugin-task-cannot-be-found/

Solution – make the crmregister file read only or check it out of source control

 

Isolation Mode = “Sandbox”

You can also get errors if you haven’t set the isolation mode to the correct value.  Often this can be resolved by setting the isolation mode to none

CRM 2011 – plugins and isolation mode

Solution – change isolation mode = “none”

 

Error message “Assembly must be registered in isolation” when registering Plugins in Microsoft Dynamics CRM 2011

https://crmbusiness.wordpress.com/2012/04/20/crm-2011-assembly-must-be-registered-in-isolation/

Solution – Give the user Deployment Administrator role

 

Type Name incorrect

I added in my custom Workflow into my CRM Developer project.  It wrote a line to the RegisterFile.crmregister but

TypeName=”Maintenance.CWA.MaintenancePublish”

TypeName=”Hosk.Dynamics.Crm.Project.MaintenancePublish”

The CRM Developer toolkit didn’t put in the name space in front of the type.

Solution – Change the typename to include the namespace

 

Developer Toolkit

The CRM Developer toolkit is great and I love it, most of the time.  Sometimes I can get very frustrated with it, which you can read about here

What would be really good is if there was a way to regenerate the RegisterFile.crmregister from the plugins deployed in the CRM organisation.

I initially thought my problems were due to the CRM Developer toolkit getting itself into a pickle, but it turned out to be something more straight forward

 

Cannot register or unregister plugins!

The code I wrote was replacing a plugin with custom workflow.  The reasonfor this is by default plugins must run within 2 minutes or they time out.  The plugin was doing a lot of looping/recusive code, updating lots of related entities. This was causing an SQL timeout error.

To get round this I  created my custom workflow, the next step was to unregister the plugin but when I tried I got an error.

Not have enough privilege to complete Create operation for an SDK entity

 

Isolation mode

Earlier in the week the project had decided to change the plugins from

IsolationMode=”Sandbox”

to

IsolationMode=”None”

 

When you change this isolated setting to none this change the security privileges you need to deploy plugins.

Like most CRM problems the first place I search (and usually find answers) is the Hosk CRM blog

https://crmbusiness.wordpress.com/2012/04/20/crm-2011-assembly-must-be-registered-in-isolation/

 

The short answer is plugins with Isolation mode set to Sandbox can be registered by a user with the role of System Administrator.

Plugins with Isolation mode = “none” have to be a deployment administrator

Why X 5

Good CRM Developers are always asking why, 5 times according to wiki

http://en.wikipedia.org/wiki/5_Whys

 

Plugin Security Restrictions

There is a Microsoft a document/web page called

Register and Deploy Plug-Ins

No one ever reads this because it’s very long and detailed and you don’t need to understand all of it to create and register plugins (unless you have a problem of course).

Reading pages such as this one does help to increase your knowledge and understanding of how CRM works, which is why I am looking at it today.  I know there is a problem because I can’t undeploy my plugin or deploy my custom workflow but

 

WHY, WHY, WHY, WHY, WHY?

Below is the section called Security Restrictions

 

Security Restrictions

There is a security restriction that enables only privileged users to register plug-ins. For plug-ins that are not registered in isolation, the system user account under which the plug-in is being registered must exist in the Deployment Administrators group of Deployment Manager. Only the System Administrator user account or any user account included in the Deployment Administrators group can run Deployment Manager.

Important
For non-isolated plug-ins, failure to include the registering user account in the Deployment Administrators group results in an exception being thrown during plug-in registration. The exception description states “Not have enough privilege to complete Create operation for an SDK entity.”

The system user account under which the plug-in is being registered must have the following organization-wide security privileges:

  • prvCreatePluginAssembly
  • prvCreatePluginType
  • prvCreateSdkMessageProcessingStep
  • prvCreateSdkMessageProcessingStepImage
  • prvCreateSdkMessageProcessingStepSecureConfig

Deployment Administrator

 

Deployment Administrator is a special role which can only be given out by a deployment administrator.  When you install CRM the account used to install CRM will be given the only deployment administrator role.  This user can open up the Deployment Manager application which is found on the CRM server.  Here the you can add more Deployment Administrators

If you want to learn more about Deployment Administrator role read the blog below

CRM 2011/2013 – Understanding and adding the deployment administrator role

 

Why am I talking about deployment administrators

In the security section above, there is this important paragraph.

For non-isolated plug-ins, failure to include the registering user account in the Deployment Administrators group results in an exception being thrown during plug-in registration. The exception description states “Not have enough privilege to complete Create operation for an SDK entity.”

Users are often set up as deployment administrators at the start of the project.  The reason this annoying popped up for me was because we had changed the isolation setting from Sandbox to none.

Before when I was publishing plugins with Isolation mode = “sandbox”, this paragraph was relevant.

For plug-ins registered in the sandbox (isolation mode), the system user account under which the plug-in is being registered must have the System Administrator role. Membership in the Deployment Administrators group is not required.

I added the deployment administrator role to my user and I was back publishing and unpublishing plugin.

CRM 2013 Plugin – Step by Step guide for a Post Account Create Plugin using the Developer Toolkit

In the I have created a video if you want to watch me go through the process of creating the plugin. Before I start with this plugin tutorial, to understand it you will need to have gone through my previous tutorials I have three CRM videos and two blog posts

Setting up Developer Toolkit for Microsoft Dynamics CRM

blog – Setting up visual studio with the developer toolkit

video – CRM 2013 – Create a simple plugin in CRM 2013 using the CRM Development Toolkit 

blog – CRM 2013 – simple update plugin

CRM 2013 – Simple Plugin – Redeploying, improving and updating

So I will assume you have got CRM development toolkit setup and ready to rock, so now we can just focus on creating a new plugin to run on the create of an account record In this plugin we are going to create a plugin which is triggered when a new account is created.

Here is a video going through the steps

below features the steps and the code

The plugin will fire after the new account is created so it will be a post plugin message.

The plugin will then create a follow up task with a due date in two weeks time.

Open up your Visual Studio project.

Go to the CRM Explorer

click on Account right click and choose Create Plug-in

Create Account Plugin

 

For the Create Plugin Details, Account is already set but you want to

change Message to Create

Pipeline stage to Post-Operation

Create Account Plugin 1

 

This will Create a file called PostAccountCreate.cs  For this plugin I have split up the default plugin code and the code which does the creating of the task.

 

// <copyright file="PostAccountCreate.cs" company="Microsoft">
// Copyright (c) 2014 All Rights Reserved
// </copyright>
// <author>Microsoft</author>
// <date>4/26/2014 10:42:26 PM</date>
// <summary>Implements the PostAccountCreate 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 HoskCRMDev2013.Plugins.Plugin_Code;
 using Microsoft.Xrm.Sdk;

 /// <summary>
 /// PostAccountCreate Plugin.
 /// </summary>
 public class PostAccountCreate: Plugin
 {
 /// <summary>
 /// Initializes a new instance of the <see cref="PostAccountCreate"/> class.
 /// </summary>
 public PostAccountCreate()
 : base(typeof(PostAccountCreate))
 {
 base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, "Create", "account", new Action<LocalPluginContext>(ExecutePostAccountCreate)));

 // 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 ExecutePostAccountCreate(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"];
 if (entity.LogicalName == "account")
 {
 if (context.MessageName.ToUpper() == "CREATE")
 {

 AccountCreateFollowUp accountCreateFollowUp = new AccountCreateFollowUp();
 accountCreateFollowUp.CreateAccountTask(service, entity);

 }
 }
 }
 }
 }
}

The code above checks to see if the entity is of type account and if the message is a CREATE message. This is in case you had the same plugin being fired on lots of different account messages (e.g. create, update, status change).  We aren’t really using it in this plugin but I thought it would be interesting to mention it.

The code which creates the task is called here

AccountCreateFollowUp accountCreateFollowUp = new AccountCreateFollowUp();
accountCreateFollowUp.CreateAccountTask(service, entity);

Below you can see the class which creates the followup task

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;

namespace HoskCRMDev2013.Plugins.Plugin_Code
{
 public class AccountCreateFollowUp
 {
 /// <summary>
 /// Code to create account task for new accounts
 /// </summary>
 /// <param name="service">crm service</param>
 /// <param name="accountEntity">entity of the newly created account</param>
 public void CreateAccountTask(IOrganizationService service, Entity accountEntity)
 {
 try
 {

 //create new task for account set in 2 weeks in the future
 Entity contactAccountTask = new Entity("task");
 contactAccountTask["subject"] = "Check new account is happy";
 contactAccountTask["description"] =
 "Make contact with new customer. See if they are happy with service and resolve any issues.";
 contactAccountTask["scheduledstart"] = DateTime.Now.AddDays(14);
 contactAccountTask["scheduledend"] = DateTime.Now.AddDays(14);
 EntityReference entRef = new EntityReference("account", accountEntity.Id);
 contactAccountTask["regardingobjectid"] = entRef;

 // Create the task and this should be linked to the new account record
 service.Create(contactAccountTask);

 }

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

 }

}

Now right click on CRMPackage

click build

Click Deploy

 

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);
}
}
}
}
}
}