Virtual Entities for tracking recently used items

Virtual entities is a powerful feature that can be used not only to bring data from external sources, but also from inside Dynamics CRM/Dynamics 365 Customer Engagement.

Jason Lattimer already has a post (https://jlattimer.blogspot.com.au/2017/12/creating-custom-virtual-entity-data.html) that goes through how to setup the custom datasource/data provider. So, go and read that first as it has all the screenshots and I would be duplicating the content, if I go through the steps again.

Gotcha 1:

There is a exception when you create the datasource.

Datasource Creation Error

You can simply ignore this and refresh the Plugin Registration tool.

Gotcha 2: If you don’t want the user the open up an individual record, you don’t have to implement Retrieve message. It is optional. Since, I just want a collated entity, I did not register any plugin for the Retrieve.

Data Provider

Gotcha 3: You have to open up the newly created Data Provider entity, and enter the external name. If you don’t enter this, you will be unable to create the data source, as it will always error out.

Data Source Primary Key Attribute

Create New Data Source

Data Source List

Objective: MRU items should be accessible from Advanced Find. As an Administrator, I would like to query this data, and see metrics around user participation, entity usage, activity by week/month/year etc.

This is the Most Recently Used area.

Recent Items

This is the Advanced Find on the virtual entity, which is driven by the same data.

Advanced Find Results

As you can see, the data matches up. All the heavy lifting is done by the plugin, that retrieves the records from the “UserEntityUISettings” entity, parses the XML, sorts by user and accessed on and then populated the virtual entity “Recent Items”.

You can query by “Type Equals”, “User Equals” and “User Equals Current User”.

Advanced Find

I can also do a PowerBI report that is driven by the same virtual entity.

PowerBI Dashboard

Source code -> https://github.com/rajyraman/Recent-Items-Virtual-Entity

Managed Solution -> https://github.com/rajyraman/Recent-Items-Virtual-Entity/releases

I hope this helps people to use virtual entities to retrieve data from inside CRM as well – a sort of collation mechanism for reporting.

Reference:

https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/virtual-entities/sample-generic-ve-plugin

https://jlattimer.blogspot.com.au/2017/12/creating-custom-virtual-entity-data.html

Advertisements

Plugin Integration Tests using FakeXrmEasy

XrmUnitTest and FakeXrmEasy are two testing frameworks that are specifically targeted towards Dynamics CRM/Dynamics 365. It is possible to do both unit test as well as integration tests using both these frameworks. In this first post, I would like to cover FakeXrmEasy. Here is a sample plugin code that I would like to test:

using Microsoft.Xrm.Sdk;
using System;
using Microsoft.Crm.Sdk.Messages;

namespace SamplePlugin
{
    public class LastNameUpperCasePlugin : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = factory.CreateOrganizationService(context.UserId);

            try
            {
                Entity entity = (Entity)context.InputParameters["Target"];
                //not required. Just added to demonstrate that we are connecting to a real crm org service
                var response = service.Execute(new WhoAmIRequest());

                var lastName = entity.GetAttributeValue<string>("lastname");
                if (!string.IsNullOrEmpty(lastName))
                {
                    entity["lastname"] = lastName.ToUpper();
                }
            }
            catch (Exception e)
            {
                throw new InvalidPluginExecutionException(e.Message);
            }
        }
    }
}

In order to test this plugin, we have to create a unit test project. Here are the steps for it:

  1. Create a unit test  project from the existing plugin solution by right clicking and choose Add->New Project->Visual C#->Test->Unit Test Project
  2. Use nuget to add the correct FakeXrmEasy reference to the test project. In this example I am using “FakeXrmEasy.365” as I am connecting to a Dynamics 365 instance online. Refer https://www.nuget.org/profiles/jmontana to find out all the different versions that are relevant to your CRM version.
  3. Create a App.config file, if it doesn’t already exist. Here is my connection string. Refer https://msdn.microsoft.com/en-us/library/mt608573.aspx to find the correct format of the connection string for you crm instance.connection-string

Below is my test for the plugin

using System;
using System.Collections.Generic;
using System.Web.Configuration;
using FakeXrmEasy;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xrm.Sdk;

namespace SamplePlugin.Test
{
	[TestClass]
	public class LastNamePluginUnitTests
	{
		[TestMethod]
		public void Should_Set_LastName_In_UpperCase()
		{
			var context = new XrmRealContext
			{
				ProxyTypesAssembly = typeof(LastNameUpperCasePlugin).Assembly,
				ConnectionStringName = "CRMOnline"
			};
			var executionContext = context.GetDefaultPluginContext();
			var target = new Entity("contact")
			{
				["lastname"] = "Power",
				Id = Guid.NewGuid()
			};
			executionContext.MessageName = "Create";
			executionContext.Stage = 20;
			executionContext.PrimaryEntityId = target.Id;
			executionContext.PrimaryEntityName = target.LogicalName;
			executionContext.InputParameters = new ParameterCollection
			{
				new KeyValuePair<string, object>("Target", target)
			};
			context.ExecutePluginWith<LastNameUpperCasePlugin>(executionContext);
			Assert.AreEqual("POWER",
				((Entity) executionContext.InputParameters["Target"]).GetAttributeValue<string>("lastname"));
		}
	}
}

In this test I am creating a scenario where the plugin is running pre-create. You can change to add pre/post entity images or target a different message and stage. If we debug our integration test, we can see that we are connected to the real org service.

Debugger.png

I have used latebound entity in this example, but you can use the same code with early bound entities generated using crmsvcutil or Early Bound Generator.

You can use a similar approach for testing custom workflow assemblies as well. In that case you would be using “GetDefaultWorkflowContext” instead of “GetDefaultPluginContext” to get the execution context.

Footnote: Technically this test could have been better written as an unit test instead of a integration test. But the purpose of this post is to demonstrate the points below:

  1. How to build a fake plugin execution context
  2. How to use connection strings to create a real OrganizationService instance
  3. How to use the real OrganizationService with the fake plugin execution context

EDIT (22/11/2016): Thank you Jonas (@rappen) for pointing out the plugin stage should be pre-create for this to work. Updated context stage to 20.

Searching by Guid

When a lookup is resolved in CRM in advanced find on entity form, it uses quick find view to resolve the correct record. So, if you search for a value that is present in an attribute that is not a “Find Column” the value will not be resolved to a lookup. In this situation, you will get a red cross icon, indicating that this value has to be manually chosen.

Failed to resolve

Note in the above screenshot that I am searching by Guid of the contact. The primary key of the contact entity is “contactid”, but it cannot be added as a “Quick Find” column.

QuickFind Columns

The default find column of a custom entity will be “[publisher prefix]_name”. This is usually the “Primary field” of the custom entity, unless you have changed it during entity creation. If you just went with the default, this is what it will be. Unlike OOB system entities, which have lot of find columns defined by default, custom entity will have one find column i.e. primary field, defined by default.

In both these scenarios it is not possible to search by the primary key of the record, as indicated by the screenshot above. As a power user, you might know the primary key of the record you exactly want on the lookup. A common scenario that I personally experienced is that, I might create a fetchxml that returns a child result set, and I want to display the parent entity records from advanced find, that have the child lookup set to a particular value.

You could search by name if name is unique or your quickfind columns don’t return multiple results. If you search term returns multiple matches, you will get an yellow exclamation icon indicating that you would have manually resolve it.

Multiple Matches

In this situation where you are searching a contact, it is not uncommon for people to have the exact same name, and if you are searching by name (assuming fullname is a find column) you will end up in this scenario. I present a solution to this problem. It is a custom plugin that fires pre-operation of RetrieveMultiple message and modifies the query to enable searching by primary key.

This is how the plugin is registered.

Plugin

Registration

In this case, I want the primary key search capability only for account and contact, and hence registered steps only for these entities. You could add additional steps similarly for more entities, that you require.

Here is the functionality in action on lookup.

Advanced Find Guid Search

Here is the same behavior on normal saved view search.

View Guid Search

You can find the source code for the plugin at https://github.com/rajyraman/QuickFindManipulate

This a proof-of-concept to give a idea about this capability using RetrieveMultiple. You can extend on this to make the plugin steps configurable and add additional performance optimisations. I hope this will be a useful feature for power users.

Quicktip: Debugging merged plugin assembly

Plugin profiling and debugging is a very useful feature in the plugin registration tool. It helps you to debug and step through the plugin code in your local machine with Visual Studio, using the serialised plugin context. The serialised context can also be stored in CRM and debug can be initiated from this record.

Profile Screen

There is one small issue when you try to debug a IL Merged assembly. When you try to profile such a plugin assembly, you will get an error like this.

PluginRegTool Error

The error is saying that the plugin profiler cannot locate the assembly that was merged with the main plugin assembly.

Resolution:

Copy all the assemblies that were merged with the plugin assembly that you are trying to debug, into the same directory as the Plugin Registration tool executable.

I experienced this issue when I was trying to debug a plugin assembly merged using the CRM Solution Manager.

Further reading:

Debug a plugin – https://msdn.microsoft.com/en-us/library/gg328574.aspx

Gotcha: Plugin Running Async

Generally when you want run a set of tasks that are not realtime, you would do this via Workflow. On the contrary when you want something to happen straightaway, you would do this using a Plugin. Before CRM2015, there was only one type of workflow. Starting with CRM2015 a workflow can be realtime or async. A plugin also can be run asynchronously or synchronously. To summarise quickly:

  1. A realtime workflow is similar to a plugin
  2. An asynchronous plugin is similar to a workflow

Take this particular scenario: You have a plugin that runs synchronously on the Update of a certain entity. Later down the track, you decide you want this plugin to run async, as the set of tasks performed by the plugin doesn’t really have to be realtime and you want to improve the performance of the core operation.

In this scenario, you have to be mindful of one particular plugin behaviour: transaction. When an exception is thrown inside a plugin, the core operation won’t succeed as the exception in the plugin will cause the transaction to rollback. When you change the Execution Mode to “Asynchronous”, you will still see that the plugin has failed in System Jobs area, but the transaction won’t be rolled back.

For eg. take this simple code:

using Microsoft.Xrm.Sdk;
using System;

namespace CrmExperimentPlugin
{
    public class CreateContactPlugin : IPlugin
    {
        #region Secure/Unsecure Configuration Setup
        private string _secureConfig = null;
        private string _unsecureConfig = null;

        public CreateContactPlugin(string unsecureConfig, string secureConfig)
        {
            _secureConfig = secureConfig;
            _unsecureConfig = unsecureConfig;
        }
        #endregion
        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = factory.CreateOrganizationService(context.UserId);
            Entity entity = (Entity)context.InputParameters["Target"];

            Entity contact = new Entity("contact");
            contact["firstname"] = "Max";
            contact["lastname"] = "Power";
            service.Create(contact);

            throw new InvalidPluginExecutionException("Exception raised after contact create");
        }
    }
}

Assuming that the plugin is registered post-create on a particular entity and Execution Mode is “Synchronous”, there will be no contact called “Max Power” at the end of the plugin execution. But if the same plugin is registered as post-create and asynchronous, there will be a contact called “Max Power” as the exception doesn’t rollback the transaction.

If you reference Event Execution Pipeline in msdn, it says this about the plugin transaction:

Stages 20 and 40 are guaranteed to be part of the database transaction while stage 10 may be part of the transaction.

But this is not entirely true, as it also depends on the Execution Mode.

tl;dr; Register plugins to run sychronously, if you need the transaction to rollback on plugin exception and need to validate certain criteria for the core operation to succeed. If you don’t need these features, use a workflow.

Performing Outer Join in Advanced Find

It is currently not possible to perform left outer join using Advanced Find, even though that capability exists in CRM2015. For e.g. if you want to get a list of contacts, that don’t have an invoice, you cannot do it using an ad-hoc Advanced Find.

This limitation is usually overcome by creating a system view that does a regular inner join, and then updating the view’s fetchxml using a tool like FXB or even editing the customisation file directly. There is also the rollup field approach described in http://crmtipoftheday.com/2015/04/27/not-in-for-the-rest-of-us/. But if you are going to do an ad-hoc advanced find query, you really don’t want to create one system view for each entity, for which you want to do an outer join.

I present to you a solution for this problem: AdvancedQueryOuterJoin

This is a plugin that runs on the pre-stage of RetrieveMultiple, and alters the fetchxml with the correct left outer join condition. This means you can do an Advanced Find Query like this one below.

OuterJoin

The above query is for Account, who have child contacts, but don’t have any opportunities. When the plugin sees a link entity in the query, with a null primary key condition, it replaces that condition with a left outer join on the parent entity. The code itself is very simple, and you can have a look at the github repo to understand how this works.

You can download the solution from https://github.com/rajyraman/AdvancedQueryOuterJoin/releases. Download the solution file that works best for your CRM instance i.e. 7.0 or 7.1. Please also use the github issues area, for entering details about any problems that you encounter.

EDIT (01/06/15): Using this plugin will not correct the fetchxml you download by clicking the Download Fetch XML in the Advanced Find window. If you are going to run your downloaded query in FXB, make sure you change the Output type to Grid.

FXB

If you use FetchResult, you won’t see any results, as the plugin would not have executed in the background.

EDIT (02/10/15): Refer to the newer post https://dreamingincrm.com/2015/09/14/bookmarklet-advanced-find-outer-join-a-k-a-not-in-condition/ for doing this using bookmarklet.

Plugin Registration Tool and Plugin Profile

Having spent most of today trying out a failed plugin experiment, I discovered a feature of Plugin Registration tool that I never used before. It it not very clear from the SDK documentation, what keys are there in the InputParameters and OutputParameters and often I refer the appropriate request/response class, to find out what key I should be using on the InputParameter e.g. for UpdateRequest use Target key on the InputParameter for a pre-operation plugin.There seems to be a easier way to capture the plugin state right after the plugin fires. This can be done using the Plugin Registration tool. This is the plugin command bar:

Replay Plug-in Execution button is used when you want to debug the plugin in Visual Studio. This process is well documented in multiple blogs and SDK documentation. View Plug-in profile functionality is what we want to use during the initial plugin development stage. After registering a plugin, right click on the desired step and choose Start Profiling.

Persist the profile to entity, instead of throwing an exception.

Once this is done, perform the action that triggers the plugin. This creates a profile record, that we can use to dump the plugin state. After the plugin has finished executing, click the view plugin profile button in the command bar. It brings up this window.

Next, click the downarrow button and choose the appropriate trace record.

After clicking the View button, you can see the entire plugin state. The below is the xml I got for a plugin firing on Retrieve message on SavedQuery.

<Profile>
  <Configuration i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
  <ConstructorDurationInMilliseconds>2</ConstructorDurationInMilliseconds>
  <ConstructorException i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
  <ConstructorStartTime>2015-05-06T01:46:45.5675566Z</ConstructorStartTime>
  <Context>
    <z:anyType i:type="PluginExecutionContext" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <BusinessUnitId>1f06daae-de79-e411-80bd-00155dc6799a</BusinessUnitId>
      <CorrelationId>09993a86-41c8-40b7-bc4c-2717057b5408</CorrelationId>
      <Depth>1</Depth>
      <InitiatingUserId>af20daae-de79-e411-80bd-00155dc6799a</InitiatingUserId>
      <InputParameters xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
        <a:KeyValuePairOfstringanyType>
          <b:key>Target</b:key>
          <b:value i:type="a:EntityReference">
            <a:Id>00000000-0000-0000-00aa-000010001001</a:Id>
            <a:LogicalName>savedquery</a:LogicalName>
            <a:Name i:nil="true" />
          </b:value>
        </a:KeyValuePairOfstringanyType>
        <a:KeyValuePairOfstringanyType>
          <b:key>ColumnSet</b:key>
          <b:value i:type="a:ColumnSet">
            <a:AllColumns>false</a:AllColumns>
            <a:Columns xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
              <c:string>fetchxml</c:string>
              <c:string>name</c:string>
              <c:string>description</c:string>
              <c:string>returnedtypecode</c:string>
              <c:string>layoutxml</c:string>
              <c:string>savedqueryid</c:string>
              <c:string>name</c:string>
              <c:string>statecode</c:string>
              <c:string>statuscode</c:string>
              <c:string>ismanaged</c:string>
              <c:string>iscustomizable</c:string>
            </a:Columns>
          </b:value>
        </a:KeyValuePairOfstringanyType>
        <a:KeyValuePairOfstringanyType>
          <b:key>ReturnNotifications</b:key>
          <b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">true</b:value>
        </a:KeyValuePairOfstringanyType>
        <a:KeyValuePairOfstringanyType>
          <b:key>RelatedEntitiesQuery</b:key>
          <b:value i:nil="true" />
        </a:KeyValuePairOfstringanyType>
      </InputParameters>
      <IsExecutingOffline>false</IsExecutingOffline>
      <IsInTransaction>false</IsInTransaction>
      <IsOfflinePlayback>false</IsOfflinePlayback>
      <IsolationMode>2</IsolationMode>
      <MessageName>Retrieve</MessageName>
      <Mode>0</Mode>
      <OperationCreatedOn>2015-05-06T01:46:45.5206856Z</OperationCreatedOn>
      <OperationId>00000000-0000-0000-0000-000000000000</OperationId>
      <OrganizationId>a240c2fd-16a0-4ab5-9ecb-a43a6afdcc8c</OrganizationId>
      <OrganizationName>Contoso</OrganizationName>
      <OutputParameters xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
        <a:KeyValuePairOfstringanyType>
          <b:key>BusinessEntity</b:key>
          <b:value i:type="a:Entity">
            <a:Attributes>
              <a:KeyValuePairOfstringanyType>
                <b:key>savedqueryid</b:key>
                <b:value i:type="z:guid">00000000-0000-0000-00aa-000010001001</b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>fetchxml</b:key>
                <b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema"><fetch version="1.0" output-format="xml-platform" mapping="logical"><entity name="account"><attribute name="name" /><attribute name="address1_city" /><order attribute="name" descending="false" /><filter type="and"><condition attribute="ownerid" operator="eq-userid" /><condition attribute="statecode" operator="eq" value="0" /></filter><attribute name="primarycontactid" /><attribute name="telephone1" /><attribute name="accountid" /><link-entity alias="accountprimarycontactidcontactcontactid" name="contact" from="contactid" to="primarycontactid" link-type="outer" visible="false"><attribute name="emailaddress1" /></link-entity></entity></fetch></b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>returnedtypecode</b:key>
                <b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">account</b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>name</b:key>
                <b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">My Active Accounts</b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>statuscode</b:key>
                <b:value i:type="a:OptionSetValue">
                  <a:Value>1</a:Value>
                </b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>iscustomizable</b:key>
                <b:value i:type="a:BooleanManagedProperty">
                  <a:CanBeChanged>true</a:CanBeChanged>
                  <a:ManagedPropertyLogicalName>iscustomizableanddeletable</a:ManagedPropertyLogicalName>
                  <a:Value>true</a:Value>
                </b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>layoutxml</b:key>
                <b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema"><grid name="resultset" object="1" jump="name" select="1" icon="1" preview="1"><row name="result" id="accountid"><cell name="name" width="300" /><cell name="telephone1" width="100" /><cell name="address1_city" width="100" /><cell name="primarycontactid" width="150" /><cell name="accountprimarycontactidcontactcontactid.emailaddress1" width="150" disableSorting="1" /></row></grid></b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>ismanaged</b:key>
                <b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">true</b:value>
              </a:KeyValuePairOfstringanyType>
              <a:KeyValuePairOfstringanyType>
                <b:key>statecode</b:key>
                <b:value i:type="a:OptionSetValue">
                  <a:Value>0</a:Value>
                </b:value>
              </a:KeyValuePairOfstringanyType>
            </a:Attributes>
            <a:EntityState i:nil="true" />
            <a:FormattedValues>
              <a:KeyValuePairOfstringstring>
                <b:key>returnedtypecode</b:key>
                <b:value>Account</b:value>
              </a:KeyValuePairOfstringstring>
              <a:KeyValuePairOfstringstring>
                <b:key>statuscode</b:key>
                <b:value>Active</b:value>
              </a:KeyValuePairOfstringstring>
              <a:KeyValuePairOfstringstring>
                <b:key>iscustomizable</b:key>
                <b:value>True</b:value>
              </a:KeyValuePairOfstringstring>
              <a:KeyValuePairOfstringstring>
                <b:key>ismanaged</b:key>
                <b:value>Managed</b:value>
              </a:KeyValuePairOfstringstring>
              <a:KeyValuePairOfstringstring>
                <b:key>statecode</b:key>
                <b:value>Active</b:value>
              </a:KeyValuePairOfstringstring>
            </a:FormattedValues>
            <a:Id>00000000-0000-0000-00aa-000010001001</a:Id>
            <a:LogicalName>savedquery</a:LogicalName>
            <a:RelatedEntities />
          </b:value>
        </a:KeyValuePairOfstringanyType>
      </OutputParameters>
      <OwningExtension xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
        <a:Id>517294bb-91f3-e411-80d0-00155dc6799a</a:Id>
        <a:LogicalName>sdkmessageprocessingstep</a:LogicalName>
        <a:Name>RYR.VirtualViews.SavedQueryOnRetrieveMultiplePlugin: Retrieve of savedquery (Profiler)</a:Name>
      </OwningExtension>
      <PostEntityImages xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
      <PreEntityImages xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
      <PrimaryEntityId>00000000-0000-0000-00aa-000010001001</PrimaryEntityId>
      <PrimaryEntityName>savedquery</PrimaryEntityName>
      <RequestId i:nil="true" />
      <SecondaryEntityName>none</SecondaryEntityName>
      <SharedVariables xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
      <UserId>af20daae-de79-e411-80bd-00155dc6799a</UserId>
      <ParentContext i:nil="true" />
      <Stage>40</Stage>
    </z:anyType>
  </Context>
  <ExecutionDurationInMilliseconds>8</ExecutionDurationInMilliseconds>
  <ExecutionException i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
  <ExecutionStartTime>2015-05-06T01:46:45.5675566Z</ExecutionStartTime>
  <HasServiceEndpointNotificationService>true</HasServiceEndpointNotificationService>
  <IsContextReplay>false</IsContextReplay>
  <IsolationMode>2</IsolationMode>
  <OperationType>Plugin</OperationType>
  <ProfileVersion>1.1</ProfileVersion>
  <ReplayEvents xmlns:a="http://schemas.datacontract.org/2004/07/PluginProfiler.Plugins" />
  <SecureConfiguration i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
  <TypeName>RYR.VirtualViews.SavedQueryOnRetrieveMultiplePlugin</TypeName>
  <WorkflowInputParameters xmlns:a="http://schemas.datacontract.org/2004/07/PluginProfiler.Plugins" />
  <WorkflowOutputParameters xmlns:a="http://schemas.datacontract.org/2004/07/PluginProfiler.Plugins" />
  <WorkflowStepId i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
</Profile>

As you can see, this file is quite useful in trying to understand the plugin execution context and can be beneficial in the initial development stage. This feature has been in PluginRegistration tool since CRM2011, and I felt like an idiot for having just discovered it, but happy that I finally discovered this. I will definitely be using this a lot in the coming days.