I have been really excited recently, as I began working more closely with ADXstudio & CRM Portals. This was perhaps one of the more exciting new features introduced as part of the CRM 2016 Spring Wave earlier this year and presents a major step forward for the CRM product. Now, businesses can start to leverage their CRM and its data in developing professional, accessible, customer and/or partner facing websites that integrate natively with your existing CRM system and processes.

One of the first challenges when getting to grips with portals is how you go about changing the OOB design of your site – for example, how do you modify the colour for a background or a particular button? Both products utilise Bootstrap to enable portal designers to very quickly develop professional looking portals that are customised to suit particular businesses design/theming preferences. In addition to this, as outlined on ADXstudio’s website:

By using Bootstrap’s layout system, it’s possible to develop a single site that presents an appropriate interface to all devices your customers might use.

In the days where mobile responsiveness is an absolute requirement for website projects, Bootstrap provides a framework that ensures a consistent UI experience is maintained at all times for your end users. But, if you are scratching your head at just how to start working with Bootstrap (like I recently was!), it can be a major hurdle figuring out how to begin styling your portal. In this week’s blog post, I will take a closer look at what Bootstrap is, what “shortcuts” are out there that can greatly speed up developing your first Bootstrap template and how you go about applying this to your custom portal site:

What is Bootstrap?

Bootstrap was originally developed by Twitter but has since become an open-source project, on its 3rd major version. The Bootstrap website contains a nice little summary around the history of the project and its interesting journey from internal project to open-source release:

Bootstrap was created at Twitter in mid-2010 by @mdo and @fat. Prior to being an open-sourced framework, Bootstrap was known as Twitter Blueprint. A few months into development, Twitter held its first Hack Week and the project exploded as developers of all skill levels jumped in without any external guidance. It served as the style guide for internal tools development at the company for over a year before its public release, and continues to do so today.

Source: http://getbootstrap.com/about/

I was actually surprised to learn about its popularity, having not had any previous exposure to it, and its status as the almost de-facto standard in website design circles.

Creating your first Bootstrap Template

Microsoft’s article on portal theming (replicated from ADXStudios original article) suggests a few different websites to try in the first instance. We’ll take a closer look at 3 of the websites mentioned here that enable you to customise a Bootstrap template from scratch, and then at an alternative route not mentioned on either website:

Official Bootstrap Customizer

Via the official Bootstrap website, you can create your own custom bootstrap template. As pointed out in the above articles, there is no GUI interface that lets you preview your customised template; which means you have to download and apply the Bootstrap files to your website in order to get a feel for how it looks. If you’re feeling particular masochistic, then this is definitely the route for you 🙂

6

Argh, too many settings! (╯°□°)╯︵ ┻━┻

BootSwatchr

When you first start using BootSwatchr, you immediately warm to it straight away – it provides a WYSIWYG editor, enabling you to very quickly tinker around with the various settings on the default Bootstrap template, and see how they will look in a browser. VERY nice! However, I have encountered some issue using the tool on Internet Explorer/Microsoft Edge, particuarly when it comes to the most crucial bit – exporting your BootStrap template .css file using the ‘Get CSS…‘ button. This is definitely one of the tools you should check out in the first instance, but just be aware that it may not work correctly on your browser of choice.

7

Code editor? Check. WYSIWYG view? Check. Instant applying of changes to the preview? Check. Full marks!

BootTheme

What is encouraging when you first start using BootTheme is that it looks to be constructed in the same vein as BootSwatch. However, I have had real trouble figuring out how to use this tool, as it is initially quite daunting figuring out where to start. I think with some dedicated time and learning, this could be a really effective tool to use when building your Bootstrap templates, but perhaps not the best beginners tool.

8

So er, yeah, looks pretty good – where do I start? :S

PaintStrap/COLOURLovers

These tools are the ones that I have used when creating Bootstrap templates, and used in tandem, are quite effective in creating a Bootstrap template that conforms to specific branding requirements. You start off by creating a ColourLovers account, which lets you then create a “colour scheme” that can be used in PaintStrap. A “colour scheme” is essentially a collection of up to 5 different colours that you want to use on your BootStrap, which can be generated very easily using the ColourLovers website. Simply go to Create -> Palette, specify your colours, give it a descriptive name and save onto your profile:

1011 12

Once you’ve saved your COLOURLovers template, you then copy across the 6 digit code for the template into the PaintStrap 3 step wizard. This can be found within the URL of your selected template:

13

Which is then entered onto PaintStrap:

14

Then, on Step 2, you can start to customise which colours will appear where on your BootStrap – the nice thing being that you are not restricted to just the colours on your template and you can get a partial and full-screen preview of your template as you build it:

9

When you are happy with your template, click the ‘Generate CSS!’ button to download your BootStrap .css file. You will want to grab the bootstrap.min.css file; this is exactly the same as the bootstrap.css file but has been slimmed down to remove line breaks, whitespace etc.

Once you’ve got your Bootstrap, how do you apply it to your portal?

Let’s assume we are using the Basic Portal, provided as part of ADXStudio; the steps are the same for CRM Portals, rest assured:

3

Navigate to the home page of your portal, ensuring that you are logged in as a Contact that has the Administrators Web Role. On the ADX widget on the top right of your page, click on New -> Child file:

1

You’ll be greeted with the Create a new child file screen. Populate the details as follows:

  • Name: bootstrap.min.css
  • Upload File: Browse and select your bootstrap.min.css file
  • Partial URL: bootstrap.min.css
  • Hidden from Sitemap: Tick the box

It should look something like this:

2

Press Save and then refresh your browser. Your new bootstrap will be applied to your site; and, because we have configured it on our home page, it will cascade automatically across our portal site:

4 5
Looks…err…interesting! I would not recommend or endorse creating a bootstrap that looks like this, but this example provides an excellent illustration of the versatility of Bootstrap.

Getting up to speed with Bootstrap may look quite daunting initially, but fortunately, there are lots of tools and resources available online that can get you running quickly with BootStrap. These tools can significantly ease your learning journey with ADXstudio/CRM Portals and also allows you to look like a website design whizz in the process 🙂

One factor that anyone considering CRM Online for their business would need to address is determining the amount of data you are intending to bring across into the CRM and how you expect this to expand in the long term. CRM Online includes 5GB of database storage for all of your CRM instances on an Office 365 tenant, which is included as part of the base plan (i.e. 5 Professional Licenses). If you run out of storage, there are only two ways in which you can increase this:

  • By purchasing 20 Professional user licenses, you will be granted an additional 2.5GB storage. This is cumulative up to a maximum of 50GB (e.g. if you add an additional 20 Professional licenses to your existing 400, then your storage will not increase further)
  • Additional storage can be purchased, at a whopping £6.20 per GB

Based on the above costs, a careful analysis needs to be performed to ensure that a migration does not end up costing more than expected; either in the immediate or long term. In any event, administrators will need to keep a careful eye on their database storage levels over time by frequently reviewing the CRM Online Administration Center on their deployment:

5

I was recently investigating why a particular CRM Online deployments storage had ballooned from a rather modest 0.75GB all the way up to nearly 4GB – all over the space of a few months! After doing some further digging, I determined that the Attachment entity was consuming the most amount of data in the CRM instance. This was not spotted readily, due to the rather strange way that this entity works within CRM, which I wanted to highlight and discuss further as part of this weeks blog post:

Why should administrators regularly check Attachment Entity records?

Every time an email is automatically synced into CRM, either with Server-Side Synchronisation or via CRM for Outlook, all the attachments are also brought into CRM. This will include your typical files (a .pdf document, an Excel worksheet etc.); but may also include images from your email signature or any other content on the email that is not stored in plain text. Over time and, in particularly large deployments, this can start to eat away at your database storage very quickly. Ignore the impact that this entity can have on your CRM deployment at your peril…

Why administrators may struggle to regularly check Attachment Entity records…

The attachment entity cannot be searched using Advanced Find. It’s just not there on the list of entities available to query:

3

Oh, Attachment, where art thou?

The only way within the CRM interface to get a rough indication of how many attachment records are stored within the database is by running an Advanced Find Query against the Email entity, filtering out only email records that have a related attachment record:

4

(You can then customise the File Size (Bytes) filter to search for emails where the attachment is over a certain size – then, drill down in the email record to see the actual Attachment records).

So, what other options are available to us? You can use FetchXML to query the data on an Online/On-Premise deployment, but you are restricted in what information is returned. Only the following fields will be returned by a FetchXML; even if you specify, for example, the activityid field, it will never be returned in your request:

  • mimetype
  • filename
  • attachmentid
  • body
  • filesize

So with these restrictions in mind, you can run the below FetchXML query to return all attachment records where the file size is over 50MB:

<fetch>
  <entity name="attachment" >
    <attribute name="attachmentid" />
    <attribute name="filename" />
    <attribute name="filesize" />
    <filter type="and" >
      <condition attribute="filesize" operator="gt" value="10485760" />
    </filter>
    <order attribute="filesize" descending="true" />
  </entity>
</fetch>

To actually delete the record, you would have to either locate the record within CRM, a potentially laborious task. Unfortunately, there is no way to navigate to the record using query parameters appended to your CRM URL or to even use a Delete request in the SDK to remove the records. If you are running On-Premise CRM, then you could always run this manual query against the CRM organisation database:

USE MyCRM_MSCRM

GO

DELETE FROM dbo.ActivityMimeAttachment
WHERE AttachmentID = 'B18FBFCA-046D-E611-80CB-00155D02DD0D'

I am not sure whether this a supported way of using the application, so use at your own risk 🙂

Be Proactive: 2 Tips to reduce the Attachment Data Footprint

To avoid a situation where you are having to go through the above to delete unnecessary email attachments, there are 2 things you can do to try and avoid this entity from swelling up unexpectedly:

  1. In Settings -> Administration -> System Settings, on the General tab, you can specify the list of blocked file extensions for attachments. Out of the box, CRM automatically populates this with a list of the most harmful file types, as a semi-colon delimited list:1You are able to freely modify this list to include/remove the list of attachments that you want CRM to automatically strip out. So if for example, your organisation uses high resolution .png images as part of your email signatures, this may be a good candidate to add to this list.
  2. Staying within the System Settings area, but this time jumping across to the Email tab, you have an option where you can specify the maximum file size for all attachments saved in the system:2By default, this is set to 5.1MB – I would not recommend increasing this to any large degree, and there may be a case to reduce this further to around the 2-3MB mark.

Consider all things as well – how essential is it that any attachment is stored within CRM in the first place?

Certain organisations may use separate email archiving solutions, that automatically backup and store and all emails sent across an organisation into an archive that can then be accessed by end users and (most importantly) system administrators. Examples of these solutions may include Exchange Online Archiving, MailStore, Mimecast or Metalogix. If you know that your organisation has one of these solutions in place and that emails are being backed up/archived with all of their appropriate attachments, then it may be prudent to block CRM from storing any type of attachment file altogether. On the flip-side, if your organisation does not have such a solution in place, then this can work in reverse – CRM could act as an excellent way of implementing an email archiving-lite solution for your business. Although, I would expect that this is only practical for an On-Premise deployment, where your storage costs will be typically more cost-effective compared to CRM Online.

I was writing some code recently for a CRM plugin, that involved working with multiple link entity attributes as part of a QueryExpression. Accomplishing this is relatively straightforward, thanks to some of the code samples provided within the SDK. For example, the following sample code gives you everything you would need; how to build the query and, more importantly, include the attributes you would need to return as part of the request:

//Create a query expression specifying the link entity alias and the columns of the link entity that you want to return
QueryExpression qe = new QueryExpression();
qe.EntityName = "account";
qe.ColumnSet = new ColumnSet();
qe.ColumnSet.Columns.Add("name");

qe.LinkEntities.Add(new LinkEntity("account", "contact", "primarycontactid", "contactid", JoinOperator.Inner));
qe.LinkEntities[0].Columns.AddColumns("firstname", "lastname");
qe.LinkEntities[0].EntityAlias = "primarycontact";

EntityCollection ec = _orgService.RetrieveMultiple(qe);

Console.WriteLine("Retrieved {0} entities", ec.Entities.Count);
foreach (Entity act in ec.Entities)
    {
       Console.WriteLine("account name:" + act["name"]);
       Console.WriteLine("primary contact first name:" + act["primarycontact.firstname"]);
       Console.WriteLine("primary contact last name:" + act["primarycontact.lastname"]);
    }

Very handy! But there are two problems that you may encounter when attempting to run the code yourself:

  • The code snippet attempts to print out the value of the LinkEntity First Name and Last Name Contact fields to the console. So, for example, when your code retrieves the Fourth Coffee (sample) record, we would expect to see Rene Valdes (sample) printed out. Unfortunately, this doesn’t happen, and we are instead greeted with a value that may precipitate some serious head-scratching:1The issue lies in how the SDK handles link-entity attributes included as part of a query. Instead of returning the value in the data type we would expect and, despite appending the appropriate namespace within our code to indicate the link entity our attribute is from (in this case, primarycontact), CRM instead gives us the attribute as part of an AliasedValue object. The comforting thing to know is that the attribute value that we may (desperately!) want to grab is in this object somewhere, and we will look at the best way of accessing this later on in this post.
  • Putting aside the above issue, let’s assume that we weren’t working with an AliasedValue and just a standard CRM data type – for example, Single Line of Text/string. Our above code would work fine against all of the sample data records within CRM. But if we add a new Account record and add a Primary Contact record that doesn’t have a First Name value, we get an error thrown within our code when it attempts to output this value into the console:2Regular readers of the blog may be able to guess what is happening – as part of one of my previous posts looking at the GetAttributeValue method, we looked at the danger of accessing attributes via the Entity class, particularly when these attributes may end up containing blank values. In this particular example, there is another factor at play too – when a QueryExpression attempts to bring back all of the requested attributes, any attribute(s) that do not contain a value in the CRM database (i.e. are NULL) are not returned at all. This fact is useful in ensuring that our code can run as optimally as possible, but in this case, is our downfall – because our Entity object has no Contact attribute with a First Name value, no value is available to return, so our code panics and throws an error; for the simple reason that it is attempting to access something that does not exist.

So how can we get around both of these issues? First of all, we need to alter the code so that the actual value of the First Name and Last Name fields are displayed correctly. One way of executing this is to explicitly cast our attribute values as AliasedValues, access the inner properties of the object and then cast to string the value of the attribute. An updated version of our code would, therefore, look like this:

{
    Console.WriteLine("account name:" + act["name"]);
    Console.WriteLine("primary contact first name:" + ((AliasedValue)act["primarycontact.firstname"]).Value.ToString());
    Console.WriteLine("primary contact last name:" + ((AliasedValue)act["primarycontact.lastname"]).Value.ToString());

}

Running this against our Sample Account/Contact results returns what we expect, which is good 🙂

3

But, unfortunately, does not give us a complete solution to the above as we still hit a “The given key was not present in the dictionary.” error message as before, for the same reasons:

4

 

The above method is not satisfactory then, as we would need to include additional lines of code to handle the fact that the attribute value is not present – an if…else statement or a try…catch block. If we forget to put this in, then another issue surfaces in bad code potentially seeping into a Production environment – causing errors for end-users and hours of unnecessary debugging.

Our old friend GetAttributeValue has saved the day for us previously; can it do again? We can try by using the code snippet below – specifying the fact that we are returning an AliasedValue object and then getting the inner attribute Value:

Console.WriteLine("account name:" + act["name"]);
Console.WriteLine("primary contact first name:" + act.GetAttributeValue<AliasedValue>("primarycontact.firstname").Value);
Console.WriteLine("primary contact last name:" + act.GetAttributeValue<AliasedValue>("primarycontact.lastname").Value);

The code errors again, unfortunately, with a different error message, indicating that a NULL value is attempting to be accessed:

5

In this case, we must rely on our knowledge of C# to save the day, with a few options at our disposal. I’ve already suggested two possible options, but another could be considered, which would reduce the number of lines in our code. By using the conditional operator (?:), we can “test” for a NULL value in our GetAttributeValue and, if a NULL value is present, substitute it for an empty string or a value of our choice. Our final, error-free code, would look like this:

foreach (Entity act in ec.Entities)
    {

        Console.WriteLine("account name:" + act["name"]);
        Console.WriteLine("primary contact first name:" + (act.GetAttributeValue<AliasedValue>("primarycontact.firstname") == null ? "" : act.GetAttributeValue<AliasedValue>("primarycontact.firstname").Value));
        Console.WriteLine("primary contact last name:" + (act.GetAttributeValue<AliasedValue>("primarycontact.lastname") == null ? "" : act.GetAttributeValue<AliasedValue>("primarycontact.lastname").Value));

    }

Then, just to be sure, we run a quick test and confirm everything works as expected:

6

Now we can rejoice that our code is error free and that we’ve found another good example of how the GetAttributeValue method should always be used when working with CRM attributes. It is a shame though that the code above, provided by Microsoft as part of the SDK, has such a significant error within it. Hopefully, it will be addressed as part of a future version of the SDK and we should be thankful that we at least have all of the sample code in the SDK in the first place; it gives developers and CRM customisers a great starting point to start developing consistent and supported code.

A colleague recently asked me this question, and I’ll admit that it took me a few minutes to think about the answer. Learning how to write code that extends functionality within or outside of CRM is not something that you can just pick up from scratch. You usually need to have good experience with coding first, before you can safely venture into writing your first plugin or form level JScript function. Fundamental, and arguably, crucial knowledge of the CRM platform is essential too, as this ensures that you don’t put forward solutions that the application can handle natively. In this week’s blog post, I will first clarify what CRM Development actually means, before outlining  my “top tips” on how you can develop your skills to become a superstar CRM Developer.

So what is CRM Development, and is it the same as CRM Customisation?

It’s important that we first clarify what the difference is between these two types of activities, as although there is some cross-over, often they are split out into two distinct roles – a CRM Customiser and CRM Developer. Someone who occupies the first of these roles frequently spends the majority of their time working with CRM Solutions and the Customizations area of CRM. Customisers will commonly be involved in the creation of new entities, fields, system views, processes, business rule & workflows, to name a few. As a consequence, they will more often than not have a great deal knowledge of what the platform is capable of and are generally in the best position to offer support and mentoring to colleagues who are struggling with something in CRM (for example, how to create a personal view).

In comparison, a CRM Developer may spend very little time working with solutions and customisations; although they will be expected to have a general awareness of what the platform is capable of doing,  they will mostly only ever be concerned with modifying plug-ins, plug-in steps and web resources from within solutions. CRM Development instead encompasses a broad canvas of work, all of which is geared towards extending the native functionality of the application. An example list may include:

  • Writing form level JScript, for scenarios where a Business Rule can’t achieve the desired results (we’ve already learned the importance of considering Business Rules as a first step option in these scenarios)
  • Developing custom plug-ins in C#/VB.NET to execute at specific trigger points within the underlying database transaction e.g. after a Contact record has been updated.
  • Building custom workflow assemblies in C#/VB.NET to further enhance the options available as part of a workflow or dialog.
  • Setting up custom Web Resources in HTML/Silverlight, that can be embedded within CRM forms or dashboards.

A good way of remembering the difference is to remember that CRM Customisation can all be done from within the application itself, whereas CRM Development involves work created outside the application to achieve specific business requirements.

Now that we’ve got that out of the way, here’s my top 5 list on how you can learn CRM Development:

Learn C# First

As well as providing you with everything you would ever need from a plug-in/custom workflow development perspective, having a good grasp of C# can make learning JScript a lot more easier. Both languages have a lot of similarity, with some important differences that require noting. First, JScript is largely indifferent when it comes to working with data types, whereas C# is very fussy when it comes to declaring and casting your data types correctly. Secondly, whereas C# development work can be assisted via the use of early-bound class files, JScript can be annoyingly unsympathetic when you write code, with errors only cropping up when you attempt to run your code. Putting aside these differences though, being able to say that you have a good grasp of C# on your C.V. can assist greatly when seeking out roles involving CRM, particularly given such roles will be looking for experience of integrating CRM with third party applications; C# is your Swiss army knife in these situations.

The SDK is your treasure trove.

There are countless number of code examples & snippets enclosed within the SDK, which include all of the languages that you would use to extend CRM – JScript, C# and even VB.NET! These are typically in a state where you can easily deploy them to a test CRM environment, execute them and then playback within Visual Studio via the Plugin Profiler, so you can understand what they are doing.  The enclosed help file (which is replicated fully on the MSDN website) is also really detailed in explaining what you can do when developing for CRM. You can download the latest version of the SDK (updated recently for the 2016 Spring Wave) here.

Get an MSDN Subscription

I have extolled the virtues of what an MSDN subscription can provide to Microsoft professionals previously, so I won’t cover old ground. What I will highlight from this is that the Imagine Academy, included as part of a subscription, contains nearly all of the courses found on the Dynamics Learning Portal (available to CRM partners as a learning resource “hub” for all Dynamics products). It also gives you access to a number of important, developer-focused resources that you add to your arsenal and use to further enhance your knowledge of C#, JScript etc. If you’re fortunate enough to have enough money to obtain an Enterprise MSDN Subscription, or your employer has a few spare licenses, then you will be able to get your hands on a coveted CRM On-Premise license key as well. Working with the application in whatever capacity you can is the best and surest way to learn, as opposed to simply watching videos and reading online articles.

Pass those Exams

There are a wide plethora of different CRM exams available to take currently, and it can be quite confusing deciding which ones will benefit you best on your road to become a CRM Developer. I would suggest that the best exams to target a passing mark on would be the following:

The question you may be asking though is “How important are exams, compared with actual work experience?”. I have heard many debates surrounding the importance of certification, on both sides of the argument; one criticism is that they are generally not a good way equipping candidates with the practical, real-life knowledge and experience that ultimately must come to the fore when working with CRM on a day-to-day basis. Another argument against them is that they can sometimes draw you towards focusing on features of a particular application that is either not very good or is done much better by an alternative product. Notwithstanding this, I think exams hold an important place in demonstrating to colleagues and potential recruiters just how serious you are about your career and in ensuring that you keep yourself up-to-date with the appropriate technology – in these modern times, staying off the ball for as little as a month can put you behind! Going back to the original purpose of this post, the curriculum on both of these exams will leave you in a position where you have achieved a good balance of knowledge: both of what CRM, as a platform, is capable of out of the box, and what you can do to develop further solutions for the application.

And finally, believe in yourself

This last tip may sound a little bit clichéd, but achieving your desire to become good at CRM Development is something that only you have control over. The journey may be hard, and you will often fail more than you succeed at first; but if you keep working at it, never give up and, most importantly, trust in yourself and your abilities, then you will succeed in increasing your knowledge and expertise in CRM.

One of the challenges when first working with CRM Plugins is understanding how the execution context works. To briefly summarise, this contains the information relating to how and when the plugin is executed. It can give you answers to some of the questions you may wish to “ask” your CRM – for example, what record has triggered this plugin? Which CRM User forced the plugin to execute? You may also assume that the context will contain all of the fields of that entity that you can then access. This is not always the case – typically, as part of an Update request, only the fields that triggered the update will be made available. Or consider an Associate/Disassociate request (i.e. when someone relates/unrelates two records in CRM). As part of a one-to-many (1:N) relationship, only one field has changed; so that is the only field that is contained within the execution context.

The above being the case, what options do you have if you need to access fields that are not included in the context? You have two choices available to you:

  1. Obtain a reference to Organisation Service within your plugin, and then use a Retrieve request to return the fields you need from the plugin. As the context will always contain the GUID for the record that has changed, this is generally the most common way you will see this done.
  2. Utilise either a Pre or Post Entity Image of the record, which contains the attributes that you need to access.

In the past, I would always end up going down the first route. Recently though, I have been evaluating Entity Images more and more, and have begun to actively use them as part of my plugins. Entity Images are, essentially, snapshots of the CRM record that are stored on the platform either before the database operation has completed or straight after – you can decide which one you want to use, depending on what your plugin is doing. They are configured as part of deploying your plugin to your target CRM instance, and the steps involved are actually quite simple – I think its generally the case that they are not well-understood or utilised by developers who are learning about CRM for the first time.

So why should you go to the extra effort to use them within your plugins?

As alluded to above, using Pre Entity Images means you can get a snapshot of your CRM data before the data was changed. This may prove to be particularly invaluable in cases where you need to perform a before/after comparison on a particular field, and then execute a specific action either way. The only non-entity image way of doing this within CRM would be via hidden field that stores the current value of your field in question, which is referenced and updated once your business logic has completed. A slightly in-elegant solution, and one that is questionable, given that we can utilise Entity Images instead. Having ready access to the attributes which may not necessarily be exposed to the plugin when it is executed is particularly invaluable, given that this avoids a scenario where you would have to go down option 1). Disadvantages of this approach is the potential for unnecessary delays in your plugin completing and problems with your CRM instance as a whole, if your CRM’s web services are already accessed to breaking point.

There must be a catch, surely…

On the whole, it doesn’t look like it, but you do need to aware of a few things. As you may have already guessed, if your plugin runs on the Create message, you won’t be able to access any pre-image of the record; likewise, for Delete message plugins, the post-image will not be available. It should be fairly obvious why this is the case. You are also restricted with the number of Messages that support Pre/Post Images. The full list can be found here, but to summarise the most important Message types, only Update, Assign, SetState & Merge support both Image types. Bear in mind too the additional “gotchas”, which this article touches upon. If in doubt, then boot up your sandbox CRM environment and cry havoc with your test code.

Lets take a closer look at how Pre and Post Images can be implemented as part of a CRM Plugin…

The below example will compare the Pre and Post Image values of the Lead Company Name field and, if they have changed, send an email message to a Sales Manager user to alert them of this fact. Be sure to add references to the Microsoft.Xrm.Sdk and Microsoft.Crm.Sdk.Proxy .dlls from the SDK:

            //Extract the tracing service for use in debugging sandboxed plug-ins.

            ITracingService tracingService = localContext.TracingService;
            tracingService.Trace("Implemented tracing service succesfully!");

            // Obtain the execution context from the service provider.

            IPluginExecutionContext context = localContext.PluginExecutionContext;

            // Get a reference to the Organization service.

            IOrganizationService service = localContext.OrganizationService;

            if (context.InputParameters.Contains("Target"))

            {
                //Confirm that Target is actually an Entity

                if (context.InputParameters["Target"] is Entity)

                {

                    Guid _userID = context.InitiatingUserId;

                    //Retrieve the name of the user (used later)

                    Entity user = service.Retrieve("systemuser", _userID, new ColumnSet("fullname"));

                    string userName = user.GetAttributeValue<string>("fullname");

                    Entity lead = (Entity)context.InputParameters["Target"];

                    Entity preLead = (Entity)context.PreEntityImages["Image"];

                    Entity postLead = (Entity)context.PostEntityImages["Image"];

                    string preCompanyName = preLead.GetAttributeValue<string>("companyname");

                    string postCompanyName = postLead.GetAttributeValue<string>("companyname");

                    tracingService.Trace("Pre-Company Name: " + preCompanyName + " Post-Company Name: " + postCompanyName);

                    if (preCompanyName != postCompanyName)

                    {
                        tracingService.Trace("Pre-Company Name does not match Post-Company Name, alerting sales manager...");

                        //Queue ID for our Sales Manager

                        Guid _salesManagerQueueID = new Guid("41b22ba9-c866-e611-80c9-00155d02dd0d");

                        Entity fromParty = new Entity("activityparty");
                        Entity toParty = new Entity("activityparty");

                        //Email body text is in HTML

                        string emailBody = "<html lang='en'><head><meta charset='UTF-8'></head><body><p>Hello,</p><p>Please be advised that I have just changed the Company Name of a Lead record in CRM:</p><p>Lead Record URL:  <a href='http://mycrm/MyCrmInstance/main.aspx?etn=lead&pagetype=entityrecord&id=%7B" + lead.Id + "%7D'>" + postCompanyName + "</a></p><p>Old Company Name Value: " + preCompanyName + "</p><p>New Company Name Value: " + postCompanyName + "</p><p>Kind Regards</p><p>" + userName + "</p></body></html>";

                        fromParty["partyid"] = new EntityReference("systemuser", _userID);
                        toParty["partyid"] = new EntityReference("queue", _salesManagerQueueID);

                        Entity email = new Entity("email");

                        email["from"] = new Entity[] { fromParty };
                        email["to"] = new Entity[] { toParty };
                        email["subject"] = "Lead Company Name Changed";
                        email["directioncode"] = true;
                        email["description"] = emailBody;

                        //This bit just creates the e-mail record and gives us the GUID for the new record...

                        Guid _emailID = service.Create(email);

                        tracingService.Trace("Email record " + _emailID + " succesfully created.");

                        //...to actually send it, we need to use SendEmailRequest & SendEmailResponse, using the _emailID to reference the record

                        SendEmailRequest sendEmailreq = new SendEmailRequest
                        {
                            EmailId = _emailID,
                            TrackingToken = "",
                            IssueSend = true

                        };

                        SendEmailResponse sendEmailResp = (SendEmailResponse)service.Execute(sendEmailreq);

                        tracingService.Trace("Email record " + _emailID + " queued succesfully.");
                    }

                    else
                    {
                        
                        tracingService.Trace("Company Name does not appear to have changed, is this correct?");
                        return;
                    }

                    tracingService.Trace("Ending plugin execution.");

Once we have written our code and built our solution, we deploy our plugin in the normal way via the Plugin Registration Tool – ensuring that we configure our step to fire on the correct message:

1

Next, we need to configure our Pre/Post Images manually. Just because we have referenced them in our code above doesn’t mean they will automatically become available to us as our plugin is executed. Fortunately, adding them on is not too difficult and we can do it directly within the Plugin Registration Tool. First, highlight our new plugin step and select Register New Image:

2

One good thing about this is that we can configure both our Pre and Post Images in the one screen. We just confirm that our intended plugin step is selected, tick both of the boxes and ensure that the following details are completed:

  • Name: Specify a name for this image. This can be anything you want it to be, but I would recommend using the same value as Entity Alias
  • Entity Alias: This is the name that is used in our code above to reference the image. This must match exactly against this in order for the code to work correctly.
  • Parameters: Here you specify which attributes will be made available within the image. By default, all attributes are included, but you should specify only the attributes you need to work with in your plugin. For our example plugin, we only need the Company Name field, so this is the only attribute we will select.

Your settings should look something like the below:

3

With everything setup, we can now test our plugin. In this case, I have changed one of the sample data leads to force the plugin to execute:

4 5

We can then see that our Email record is successfully created, which is able to reference the value of the Company Name field before the change:

6

Playing back the plugin within Visual Studio demonstrates that our Pre and Post images are Entity objects, meaning that we can interact with them the usual way – nothing new to learn or master, which is good 🙂

7

Conclusions – or Wot I Think

Like most things with CRM, Entity Images have a time and place. If you have a desire to query additional data concerning record relationship attributes etc. as part of your plugin, then a Retrieve request is still going to be the only way you can accomplish this. There is also some additional administrative head-room required when working with Images – if, for example, you forget to specify your Pre & Post Images when deploying your plugin, then you are going to encounter immediate problems within your CRM. Having said that, I think there is an excellent case to be made to using Entity Images as much as feasibly possible in your code. A Retrieve request on a particular busy CRM platform just to return one attribute value could store up problems for your CRM instance in the long-haul; having this one value stored as part of an entity Image seems a much more sensible and logical approach. I am further struck by how useful entity images can be if you need to reference data changes before and after a record change – going back to our example above, where we would instead look at creating custom field within CRM to store this value, having it made available as part of an Entity Image could score a major time saving. Finally, you are not just restricted to using the one Pre/Post Image – you can setup multiple ones that contain different fields from your entity, that are accessed in different parts of your code, depending on your logic. I think developers working with CRM definitely owe it to themselves to check out Entity Images at least once – I think they will be pleasantly surprised at their capabilities and, one would hope, the increased performance of their CRM instance as a consequence of using them!