Author: Jonas Rapp
Developing plugins for analysis – part II
At the eXtreme365 conference in Lisbon, Portugal I did a session on plugin development with focus on adapting your code to empower the tracing and investigation features available in the Microsoft Dynamics 365 platform.
This is the second article in a series to describe the essence of that session.
Continue reading “Developing plugins for analysis – part II” I get by with a little help from my [base class]
Developing plugins for Microsoft Dynamics 365 (CRM) only using bare SDK libraries make you do the same stuff over and over.
This is why one of the first things I did when starting to work with the platform was to create a helping hand in the form of a plugin base class, implementing IPlugin.
Continue reading “I get by with a little help from my [base class]” CRM Saturday – XrmToolBox with Jonas Rapp
CRM Saturday is a recurring event where Microsoft Dynamics CRM/365 experts and MVPs gather for a day filled with sessions from both strategic and technical perspectives on everything from user adoption to plugin unit testing to IoT and intelligent analysis.
On Saturday January 28 the event was held in London, UK at the Microsoft offices in Paddington. As a “senior contributor” to the world famous XrmToolBox by MVP Tanguy Touzard I was invited to do a session on simplifying development using XrmToolBox. My session covered a brief XrmToolBox background, examples of my own favorite tools, and deep dive demos of FetchXML Builder and Plugin Trace Viewer. Of course you cannot do a demo with some customizations and plugins without using a few other XrmToolBox tools, so I did not only cover my own block busters… The presentation from the event is now available here: CRM Saturday – XrmToolBox with Jonas Rapp This contains the full presentation, and also step by step details on the demos performed, as well as some bonus demos that did not fit the tight session schedule. Note that the presentation also contains reference to a free to use GitHub repository with a simple plugin base class, that can be inherited instead of simply implementing the SDK interface IPlugin to greatly simplify plugin development and logging to the Tracing Service. The repository is available here: https://github.com/rappen/JonasPluginBase
If you would like to dig even deeper into the tracing service, XrmToolBox and the Plugin Trace Viewer – join me on my session on this topic during eXtreme365 for Partners in Lisbon, Portugal that takes place March 13-15 2017 !
If you have any questions regarding the presentation, demo or the plugin base class, don’t hesitate to contact me! More information on CRM Saturday: http://crmsaturday.com
More information on eXtreme365: http://extremecrm.com
CRM, xRM, 365, CDS – are we chasing ghosts?
Business Solutions MVP Jukka Niiranen recently wrote yet another in-depth analysis of the path Microsoft is taking from Dynamics CRM, via xRM to Dynamics 365 and the greater ecosystem including CDS, Logic Apps, Power Apps etc.
The article led to a brief but interesting Twitter-discussion between Jukka and a Swedish colleague of mine, Peter Björkmarker. The discussion ended up in another article posted on LinkedIn.
@peterbjorkmark Couldn’t fit my answer into 140 char anymore, so here we go again! https://t.co/5t3nmU9XLj @rappen— Jukka Niiranen (@jukkan) 17 november 2016
So I dug into the 140+ character reply (a bit of an understatement, indeed) to try to get a grasp of what lies ahead. I had hopes that I could come to my own personal conclusion and my own view on the future based on the new products available and the indications from Microsoft, combined with my own experience in the field. And of course trying to decode what may be hidden between the lines from this NDA-bound MVP…
Ghosts
Reading these articles definitely gives you a lot of insights. But the lingering feeling I really cannot let go of is that we are chasing ghosts. We “on the outside” try to understand where Microsoft is heading. And even an MVP for many years, who happens to be one of the most well known and respected in the field, is literally guessing what the company that dubbed him is up to. I am trying to find a valid reason for keeping secrets this way, where is the business value in keeping us in the dark? Is it to keep the interest alive? Like that tall dark stranger with the mask is the most interesting man at the party? Is it to avoid leaking secrets to competitors? All of a sudden your hottest new features are released by someone else before it has passed your own UAT? Or are they simply putting new platforms and features out there to see what happens, and take it from there? Add some whispers in the dark, throw in a few new buzz words… Have us googling, analyzing, blogging and discussing, maintain the hype for a bit longer…
Cynical
Some may call me cynical. But I have to ask myself (since there is no point asking “them”) Do they really have that Master Plan we are all trying to figure out?
Or is the secrecy simply a sales stunt? I honestly think I would prefer it being a sales stunt.
Hiding existing information of this kind from the developer community in general and the MVP group in particular seems a bit too arrogant. And probably even counter productive. They have a large community of dedicated partners, resellers, developers and MVPs that depend on Microsoft, as well as Microsoft depend on us.
Having us waste time on speculations and ISVs following the wrong guesses is neither an efficient nor a respectful way to do business.
New dimensions
Well, regardless of Microsoft’s possibly hidden agenda – of course I have some thoughts on the actual technical aspects too. I think the xRM platform is really great for a certain type of systems to support processes and information in a business. I do not think xRM will die easily, and I hope Microsoft does not have plans to kill it soon. The new possibilities opening up with CDS, Power Apps, Logical Apps, Dynamics 365 and the gigantic Azure ecosystem of services adds the means to add a few more dimensions to existing systems. Our current systems usually have two dimensions – store information and support processes.
Adding tools from the new service flora can add dimensions to open them up and really becoming alive.
Show hierarchically related activities in subgrid
In this article I will demonstrate how to implement a plugin to extend the possibilities for showing activities that are related through account hierarchy in a subgrid on a Microsoft Dynamics CRM form.
In my previous article I showed how to create a simple plugin to show all directly related activities in a subgrid, and not just activities related through the Regarding field.
Objective
The goal is to be able to display all activities (yellow boxes) anywhere below any account (blue box) that is opened in CRM. Some of you may recognize this model – it is respectfully borrowed from MVP Jukka Niiranen’s blog post on this problem: CRM 2011 subgrids ain’t what associated views used to be. As this article indicates, this has been a problem ever since we left CRM 4.0 behind. Continue reading “Show hierarchically related activities in subgrid”
Show ALL related activities in a subgrid
Microsoft Dynamics CRM offers great capabilities for activity entities, both out of the box and custom activities.
This post will describe a way to extend those capabilities even more, using a quite simple plugin.
During eXtremeCRM in Warsaw last week, a fellow CRMian and frequent user of FetchXML Builder approached me to ask if I knew a way to create “dynamic” queries with some contextual awareness. He had used unsupported methods to inject FetchXML to subgrids, to be able to show all activities related to current record, not just those where the record is related through the Regarding field. As I will show in this post, this can be accomplished with a simple plugin and a custom view.
Background
Activities handle e-mail, phonecalls, tasks, meetings, and even faxes and letters. Common custom activities are sms, transactions, and any other date-bound entity that involves participants of some kind. Participants can be contacts, accounts, leads, users, and the activities can be regarding eny entity with the “Activities” option selected. When looking at an associated view of a record to display associated activities, this can display activities where the current record is either recipient, sender, participant or regarding the activity. This is done with specialized views built into CRM, as this gives a useful overview of all activities concerning the party. The problem is when you want to have a similar view embedded on the form in a subgrid. In this case, you can only add activities related to the record through the Regarding lookup on the activities. This will of course only show a fraction of all activities that may be related to the contact, account, or any other entity form the subgrid is placed on.
Solution – the customizations
To solve this, we need to modify the query that is executed by CRM. Instead of filtering activities based on the value in the Regarding field, we want to verify that the current record is any kind of “party” to the activity. First, create a custom view for the activities, with a signature that can be identified by our plugin so that it does not trigger when we don’t want it to. Open the “All Activities” view, and select Save As to create your custom view. Add a filter criteria to only show records where Activity does not contain data. Save the view. The view is now pretty unusable. It will never show any records as activity is required for the activitypointer pseudo entity. Next, add a subgrid to the form where you want to show all activities, and select the new view as the default and only view. The customizations are now in place, and when we view any record with this form, nothing will show in the added subgrid.
Solution – the plugin
To get the query we want to be passed to CRM, we will intercept the RetrieveMultiple message in the execution pipeline before it goes to CRM. By analyzing the request we see that the query is formed like this, after conversion to FetchXML:
<fetch distinct='false' no-lock='true' mapping='logical' page='1' count='4' returntotalrecordcount='true' >
<entity name='activitypointer' >
<attribute name='subject' />
...
<filter type='and' >
<condition attribute='isregularactivity' operator='eq' value='1' />
<condition attribute='activityid' operator='null' />
<condition attribute='regardingobjectid' operator='eq' value='633929A2-F2E1-E511-8106-000D3A22EBB4' />
</filter>
<order attribute='scheduledend' descending='false' />
<link-entity name='systemuser' to='owninguser' from='systemuserid' link-type='outer' alias='activitypointerowningusersystemusersystemuserid' >
<attribute name='internalemailaddress' />
</link-entity>
</entity>
</fetch>
A bunch of included attributes have been excluded for readability
So, let’s create a plugin that triggers Pre RetrieveMultiple of activitypointer, and investigate if it has our “signature”.
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
if (context.MessageName != "RetrieveMultiple" || context.Stage != 20 || context.Mode != 0 || !context.InputParameters.Contains("Query") || !(context.InputParameters["Query"] is QueryExpression))
{
tracer.Trace("Not expected context");
return;
}
if (ReplaceRegardingCondition(query, tracer))
{
context.InputParameters["Query"] = query;
}
}
This code is pretty self-explaining. The interesting part however, comes in the ReplaceRegardingCondition method…
private static bool ReplaceRegardingCondition(QueryExpression query, ITracingService tracer)
{
if (query.EntityName != "activitypointer" || query.Criteria == null || query.Criteria.Conditions == null || query.Criteria.Conditions.Count < 2)
{
tracer.Trace("Not expected query");
return false;
}
ConditionExpression nullCondition = null;
ConditionExpression regardingCondition = null;
tracer.Trace("Checking criteria for expected conditions");
foreach (ConditionExpression cond in query.Criteria.Conditions)
{
if (cond.AttributeName == "activityid" && cond.Operator == ConditionOperator.Null)
{
tracer.Trace("Found triggering null condition");
nullCondition = cond;
}
else if (cond.AttributeName == "regardingobjectid" && cond.Operator == ConditionOperator.Equal && cond.Values.Count == 1 && cond.Values[0] is Guid)
{
tracer.Trace("Found condition for regardingobjectid");
regardingCondition = cond;
}
else
{
tracer.Trace($"Disregarding condition for {cond.AttributeName}");
}
}
if (nullCondition == null || regardingCondition == null)
{
tracer.Trace("Missing expected null condition or regardingobjectid condition");
return false;
}
var regardingId = (Guid)regardingCondition.Values[0];
tracer.Trace($"Found regarding id: {regardingId}");
tracer.Trace("Removing triggering conditions");
query.Criteria.Conditions.Remove(nullCondition);
query.Criteria.Conditions.Remove(regardingCondition);
tracer.Trace("Adding link-entity and condition for activity party");
var leActivityparty = query.AddLink("activityparty", "activityid", "activityid");
leActivityparty.LinkCriteria.AddCondition("partyid", ConditionOperator.Equal, regardingId);
return true;
}
A bit of explanation:
Line 3-7: First sanity check of the query to verify that we might find our signature.
Line 13-29: Looping through the conditions to find our signature.
Line 15-19: Identifies the null-condition that was introduced in the view.
Line 20-24: Identifies the relation for the view, used to extract the record id.
Line 30-34: Verification of our signature.
Line 35: Extract the id of the parent record which is being displayed.
Line 39: Remove the null-condition used only to identify our signature.
Line 40: Remove the condition that the activities must have current record as Regarding.
Line 43: Create link-entity to activityparty, where the current record should be found.
Line 44: Add condition that the associated party shall be the current record.
As can be seen in the Execute method above, if the query is updated by our code, the updated query will be put back into the plugin execution context, and thus the updated query is what will be executed by CRM.
Finally, the plugin shall be registered in CRM.
Result
The subgrid on the form now contains all activities that are related to the current contact, in this case.
If you activate Plug-in Trace Log for all executions, you can now see logs with something like this in the message block:
Checking criteria for expected conditions Disregarding condition for isregularactivity Found triggering null condition Found condition for regardingobjectid Found regarding id: 633929a2-f2e1-e511-8106-000d3a22ebb4 Removing triggering conditions Adding link-entity and condition for activity party
Resources
The complete source code for this plugin, including a bit more extra debugging information than shown here, can be downloaded here.
A CRM solution with the customized activity view, modified contact form, the plugin assembly and a plugin step can be downloaded here as managed version and unmanaged version.
Solution Segmentation for entity ribbons
The new feature for segmenting solutions in Microsoft Dynamics CRM is absolutely awesome.
It finally allows you to only include the parts you actually want your solution to change.
For us as an ISV this is a huge improvement. We can now deliver solutions that are completely “plug and play” and still leave a minimal footprint on the whole customer system.
RibbonDiffXml segmentation
There is however one thing that is still not possible to separate with enough granularity. To include the ribbon definition of an entity, you need to check the Include entity metadata checkbox. Unfortunately this also includes the entity display name, plural name, and all other “entity wide” settings such as Notes, Connections, Mail merge etc. The result of this is that if we include the Contact entity just to be able to add a solution specific button to the command bar, we must also include the entity settings.
Problem
When a customer has developed their own solutions, or got solutions from other third parties, we quite often face the situation where they have changed the name of an entity, e.g. Contact is renamed to Person. Now importing our solution to add this button to the contact command bar, will also change the name of the contact entity back to default, or rather to whatever it is called in our solution.
Solution
This could be solved by allowing a separate option for the RibbonDiff part of the entity definition when selecting components for the solution.
Vote!
If you agree with this proposal, you can contribute by voting up this item on Microsoft Connect.
Read more
MVP Jukka Niiranen recently wrote an excellent article describing Solution Segmentation in Microsoft Dynamics CRM.
Windows app for Microsoft Dynamics CRM in 5 minutes
Thanks to open source components for Microsoft Dynamics CRM, you can develop a WinForm application for CRM in 5 minutes.
In this blog article, I will go through a few simple steps to get up to speed developing a client that connects to and shows information from Microsoft Dynamics CRM by using two open source spinoff components: ConnectionManager from XrmToolBox and CRMGridView from FetchXML Builder. These four simple steps are all that is required:
- Create project and add NuGet packages
- Make VS aware of the imported user control
- Configure a form with CRMGridView
- Add a few lines of code
1. Create project and add NuGet packages
In Visual Studio, create a new WinForm Project. Right click the solution, select Manage NuGet Packages for Solution. In the search field, type Cinteros.Xrm Install Cinteros.Xrm.CRMWinForm. Continue reading “Windows app for Microsoft Dynamics CRM in 5 minutes”
Open Source projects and Microsoft Dynamics CRM
How, when and why can we benefit from open source project strategies in Microsoft Dynamics CRM?
I have collected my thoughts and experiences on the subject in article:
The Benefits of Open Source Projects in the World of Microsoft Dynamics CRM
published on MSDynamicsWorld.com.
Open source is not all unconditional hallelujahs |