November 9, 2017

Catch the Revise Quote message inside a Plugin

A user in Dynamics CRM/365 can revise a Quote, it's a process where the old quote is closed and a new quote is created in draft state and ready to be updated.
Dynamics doesn't support a specific message that we can intercept (in order to register a plugin step) when a quote is revised, however one of the possible ways is to deal with the Create message.

When quote are revised the integer (Whole Number) field revisionnumber is increased, this field is managed by the platform as it is not valid for Create or for Update according to its Metadata.
So when a new quote is created the revision number is 0, and the revised quotes will have 1,2,3...
Fetching the quote with the previous revision number is not a clever method to retrieve the revised quote. For example if we have an original quote (0) in Closed state and a revised quote (1) in Closed state too (because we cancelled), we are able to revise the original quote (0) and the new quote will have 2 as revision number.

My friend Daryl LaBar (@ddlabar) suggested to check the ParentContext property inside the plugin in order to access to the ReviseQuote message that initialized the create message.
In the end I wrote a plugin (registered on the Create message, Pre-Operation, Synchronous) to check the Parent Context and get the reference of the revised quote. Here the code:
public void Execute(IServiceProvider serviceProvider)
{
  try
  {
    IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

    if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
    {
      Entity currentQuote = (Entity)context.InputParameters["Target"];
      if (currentQuote.LogicalName != "quote") { return; }
      // check the revision number first
      int revisionNumber = currentQuote.GetAttributeValue<int>("revisionnumber");
      if (revisionNumber > 0)
      {
        IPluginExecutionContext parentContext = context.ParentContext;
        // check if the Parent Context contains the QuoteId parameter of the ReviseQuote message
        if (parentContext.InputParameters.Contains("QuoteId") && parentContext.InputParameters["QuoteId"] is Guid)
        {
          Guid revisedQuoteId = (Guid)parentContext.InputParameters["QuoteId"];
          IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
          IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

          Entity revisedQuote = service.Retrieve("quote", revisedQuoteId, new ColumnSet(true));
          
          // rest of the code

        }
      }
    }
  }
  catch (Exception ex)
  {
    throw new InvalidPluginExecutionException(ex.Message);
  }
}

September 21, 2017

Microsoft Portals Source Code Teardown - Episode 1 - The Solution

If you are reading this post you probably know that Microsoft released the source code of the Microsoft Portals (former ADX Studio) under the MIT license.

I have a fair experience with the code of ADX Studio 7.x but this release include also the source code of some DLLs, so I thought that analyzing parts of the source code can be interesting for Dynamics developers.

Download link: Microsoft Dynamics 365 Customer Engagement Portals Source Code
The file of interest is MicrosoftDynamics365PortalsSource.exe, after extracted the main Visual Studio solution (Portals.sln) is under the folder Solution\Portals\

In this post I will focus on the project in general, in the next episodes I will examine specific pieces of the source code that I find interesting.

The main project is MasterPortal, it is an ASP.NET MVC website configured to run with .NET Framework 4.5.2 and basically is the upgrade of what we got with the previous ADX Studio Portals.
The second project is Framework, this is the source code of the ADX Studio DLLs, like Adxstudio.Xrm.dll and the now "obsolete" Microsoft.Xrm.Client.dll, inside this project the first surprise, a project called SafeHtml, from a quick look (and as the name suggests) the purpose is to sanitize html inputs, a good candidate for a future post.

Now let's check the nuget packages, beside the standard packages used by ASP.NET MVC, the Portals solution also references these packages (this is not a complete list, only the ones that got my first attention)
  • Bond.CSharp - Bond is an open source, cross-platform framework for working with schematized data. It supports cross-language serialization/deserialization and powerful generic mechanisms for efficiently manipulating data. - I will check where this "framework" is used inside the portal
  • Lucene.net - Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users. - I worked with Lucene.net some years ago for one university exam, is a very powerful library
  • DotLiquid - DotLiquid is a templating system ported to the .NET framework from Ruby’s Liquid Markup. - So this is the library used by the Portals to handle the liquid templates
the first sneak peek inside the Portals source code ends here, see you soon!

May 24, 2017

Xrm.Page.context.getVersion is now a supported method, use it!

Recently the MSDN page regarding the Client-side context methods of Dynamics 2016/365 has been updated and thanks to the Dynamics team a new method is inside the documentation: Xrm.Page.context.getVersion.

Some developers are already using this method in their code, personally I didn't because I had not the necessity to differentiate between the 8.x endpoints.

The method returns a string containing the full version in the Major.Minor.Build.Revision format, an example is "8.2.1.185".

In the previous CRM versions if we want to get the version from the client we had two ways:
  • call the RetrieveVersionRequest message using the SOAP endpoint (example)
  • check if a specific CRM function exists (example)
I used often the second way because I always considered doing a server call (often synchronous) not a good choice for the script performance.

Why we want to know the current CRM version? Because some functions are available only from a specific version, like the one to execute a workflow: the Microsoft.Dynamics.CRM.ExecuteWorkflow introduced with the 8.2 release.

But for the Web API endpoint we need to provide only Major.Minor and not the full version, therefore a transformation is necessary.

The following code maybe will be considered exaggerated by someone but creating a robust piece of code will reduce the necessity to change the script in the future:
function getCurrentVersion(context) {
    if (context.getVersion == undefined) { return ""; }
    var versionArray = context.getVersion().split(".");
    if (versionArray.length < 2) { return ""; }
    return versionArray[0] + "." + versionArray[1]; 
}
The result from the "8.2.1.125" is "8.2" ready to be inserted inside your Web API url.

Few considerations:
  • If the version cannot be retrieved I decided to return an empty string, but you can return null, throw an exception, call another method, set a default value, etc etc...
  • the function requires the context, in this way you can use in the form scripts (like an onload event) or inside a WebResource
  • I decided to split the string to an array using the dot (.) I think this is the most robust way compared to doing a substring or expecting a second dot in the string

May 15, 2017

CRM Saturday Zurich: recap of the Community Event

Last weekend I attended #CRMSaturday in Zurich, for those not familiar with the name, CRM Saturday is a series of events across Europe (but they are going global with the July event in Australia) focusing on Dynamics 365 for Customer Engagement (or Dynamics CRM if you prefer).

Conferences and events like this are an important part of the CRM ecosystem, when you work on a platform like Dynamics 365 that implements new features rapidly, a community event can be a perfect opportunity to keep yourself up to date learning something new or go deeper with a specific functionality.

The location for the Zurich chapter was the Microsoft Offices in Switzerland, a perfect venue considering that this was a joint event with SharePoint Saturday Events, the available tracks in totally were three (2 for SharePoint and 1 for CRM).

Stefano Tempesta, one of the organizers of CRM Saturday, gave an introduction before the keynote of Kathrine Hammervold from Microsoft Norway.
The CRM track started with the session of Baris Kanlica titled "Dynamics 365 new features and deprecations", with the upcoming release this is an hot topic and personally I learned about the use of control notifications (Xrm.Page.getControl(arg).setNotification(message,uniqueId)) The next speaker was Razwan Choudry with his session about Solution Management. How to manage solutions, how to implement versioning and the infamous "Add all assets" checkbox were some of the topics covered. The message was to put more attention on the maintenance and the deployment of a solution, and I couldn’t agree more. After the lunch break was the turn of Marius Agur Pedersen with his session about Azure. He focused on Azure Service Bus and Azure Key Vault, we can bet that Azure and Dynamics 365 will be more integrated in the future. I am often inclined to don't use Azure Service Bus, but with Marius reassurances about the bus performance I will try it again. The following session was with Jordi Montaña and his testing framework Fake Xrm Easy. Unit testing is a must for medium and big projects, but should be also a priority for smaller projects that contains only a couple of plugins. The framework he created is impressive, I know how broad and different are the CRM messages and the IOrganizationService and to mock all that stuff requires countless hours. Next to the stage was Christoph Mäder with his session to improve the performance of a Dynamics CRM/365 OnPremise instance. Many customers still prefer to have their CRM in house or they are not ready to move to the Cloud, but this doesn't mean that they can't tune their instance or take advantage of some possibilities offered by an OnPremise installation. The last one to speak was Mohamed Mostafa with the other side of the medal: Considerations for Cloud Dynamics 365 Deployments. His session went through the major aspects of an Online implementation, the compromises and the big advantages to have Microsoft taking care of your instance, considering also the upcoming EU GDPR (General Data Protection Regulation). It was an amazing event and experience for me, I had the chance to met in person people that I know virtually from a long time. Despite some tweets that list me as speaker, I only attended and I was not involved in the organization of the event, all the credits goes to the other guys in the next picture, they used their time and energy to make this happen, I'm honored to know them and they are an inspiration for the community. CRM Saturday was free, so a "thank you" should be made also to the sponsors. Bottom line: attend the next CRM Saturday!

April 4, 2017

GetAttributeValue<object>, why not?

Today I wrote a small piece of code in order to replicate the exact same data from a CRM instance to another keeping the same ID, it was an exercise because I had to copy only a couple of entities, otherwise I could use one of the several tools made for this kind of operation (like KingswaySoft).

While I was writing the code I arrived to the point where I need to map the fields, so I started to write the usual GetAttributeValue<string>, GetAttributeValue<EntityReference>, ...
And I asked myself:
"source and target attributes are always of the same type, what if I use object for the Generic?"
"something like newEntity["name"] = oldEntity.GetAttributeValue<object>("name"); works?"

With a bit of surprise I found that actually works, so in the end I wrote this piece of code.
Some notes:
  • It uses CrmServiceClient, because there are two instances (source and target) one of them should contains the option RequireNewInstance = true;
  • The UpsertRequest works also when the entity has the ID defined, and not only when the entity is defined using an alternate key syntax
  • As suggested by Tinus Smith in his comment, if the purpose is to do an exact copy, it's not necessary to create the entityTarget and copy its values with GetAttributeValue<object>, instead the entitySource can be used directly inside the Target property of the UpdateRequest:
    UpsertResponse response = (UpsertResponse)target.Execute(new UpsertRequest { Target = entitySource });
MigrateEntity(source, target, "new_entity", new List { "new_name", "new_date", "new_lookupid" });

private static void MigrateEntity(CrmServiceClient source, CrmServiceClient target,
                                  string entityName, List<string> columns)
{
    QueryExpression querySource = new QueryExpression(entityName);
    querySource.ColumnSet = new ColumnSet(columns.ToArray());
    EntityCollection collSource = source.RetrieveMultiple(querySource);
    foreach (Entity entitySource in collSource.Entities)
    {
        try
        {
            Entity entityTarget = new Entity(entityName);
            entityTarget.Id = entitySource.Id;
            foreach (string column in columns)
            {
                entityTarget[column] = entitySource.GetAttributeValue<object>(column);
            }
            UpsertResponse response = (UpsertResponse)target.Execute(new UpsertRequest { Target = entityTarget });
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

January 5, 2017

MVP again for 2017, transition to the new Dynamics era

January 1st I received my third Microsoft MVP Award for my contributions to the Dynamics community, the category is still "Business Solutions" as Microsoft did not make new changes to the MVP Award structure.
It's an honor for me to be part of this group, to "celebrate" the event I will use this post to write some of my thoughts about the MVP/Dynamics world.

Currently there are a bit less than 100 MVP awarded for the Dynamics CRM competency (I don't have the exact number) but between my Twitter feed and the Dynamics Community forums I can easily write a list of another 100 great people that works with Dynamics CRM and are not MVP. They write blog posts, create useful tools, tweet relevant news and their contributions are valuable to everybody in the community. The fact that they don't have an award doesn't make their job, their opinions, their experiences less important, in the same way my contributions have not an higher value because I am an MVP, I can consider the award a small proof of my competences, but it doesn't define totally me or what I do.
The community is central, without users sharing questions/answers/thoughts, no matter if the topics are basic or advanced, the MVP program will not have reason to exist.

During the last year I had the chance to participate also to some "offline" events: April in Rome I joined the Western Europe MVP & Community Day 2016, October in Milan I joined the Microsoft Future Decoded and November in Seattle the annual MVP Summit.
All these three events were amazing, I had finally the opportunity to meet in person fantastic people and I hope to meet them soon again. If you want to get an idea you can read Leon Tribe post about the summit https://thatcrmblog.wordpress.com/2016/11/19/post-summit-debrief/

Regarding my contributions in 2016, I think the most important is the Dependent OptionSet Generator, it's a small tool but I know people using it and they find it useful, when you create a tool the goal is to simplify a process, not everybody sends spaceships to Mars (I am looking at you Elon Musk), and the saved time can be used for something else or spent outside work.
Another contribution that I am proud is that I got back the Microsoft Community Contributor (MCC) badge, it's assigned every 6 months, and in the last year my number of answers were not enough to get it.
What I failed regarding my contributions in the last year? Definitely blogging, I wrote only few posts, maybe 2017 will inspire me to write more.

2016 is also the year the Dynamics ecosystem changed and it's now toward a new direction. The "Dynamics 365" momentum now can look like just a rebranding, but it's definitely more, inside and outside of what was formerly known as Dynamics CRM. The platform is HUGE with all the available solutions like Field Service, Project Service and the Portals, it's TERRIFIC considering the integrations with PowerBI, Azure and AX, it's a GREAT future for the Dynamics platform and for the people works with it (the MVP summit was held during the US elections, so now I unintentionally use some words).

If you arrived at this point of my unexpected long post, I wish you Happy New Year and I hope you will reach your goals for 2017!

September 5, 2016

Dependent OptionSet Generator - Overview - Part 1

Some days ago I released a new Dynamics CRM tool called Dependent OptionSet Generator.
As the name suggests it helps to create and update dependent OptionSets, one of the possible approaches to filter data inside CRM UI.

Scott Durow wrote an overview about this argument on his blog: "Option-Set, Lookup or Autocomplete", it's very useful if you plan to use dependent OptionSets in order to know the pro and cons of this approach.

I want to thank Scott not only for his post but because he found the time to meet me when I was in London a couple of months ago (photo). He was the first person I talked about this little project and we had a pleasant discussion not only about Dynamics CRM, thanks again Scott!
During my short visit in London I also met again Ramón Tébar Bueno and it's really nice to spend some time with other members of the Dynamics CRM Community, plus he took me to a typical British Pub!

Now back to the main reason of this post, the Dependent OptionSet Generator.
Starting from CRM 2011 the possibility to filter OptionSets entries was available thanks to the Xrm supported methods. Microsoft also provided a solution inside their CRM SDK to implement the dependent OptionSets but the most tedious process, the generation of the XML file required to map the entries, was delegated completely to the user.

Creating manually the XML document has always been a bummer, especially when you need to deal with several entries, so developers and consultants suggest to use filtered Lookups instead.
However there are implementations where OptionSets are preferred due to the Multi-Language support, to facilitate CRM users (at eCraft, the company I work for, we often have customers with Finnish and Swedish users) or because it's required by the law (for example in some provinces of Canada).

Microsoft kept updated its Dependent OptionSet solution during the years and with the latest CRM 2016 version they changed the configuration format structure from XML to JSON, probably in order to simplify the creation of the file.

My solution consists of two parts:
  • The first part is the JavaScript Library to be included inside the CRM forms in order to filter the OptionSets
  • The second part is a Web Application to build and maintain the JSON configuration required by the JavaScript Library
This post will cover the first part, I will write another post (or more) regarding the Web Application.

The JavaScript Library included in my solution is based on the CRM 2016 SDK version called sample_SDK.DependentOptionSetSample.js, you can find the source code here. Except for the first point (Different Namespace) all the other changes were done in order to make the script compatible with CRM 2011 supported browsers.

  • Different Namespace: because I didn't change the name of the functions I preferred to use a different namespace (DO instead of SDK) in order to avoid issues if the SDK library and my library are loaded in the same form.

  • JSON: The CRM 2016 library uses JSON, but the original Microsoft Library created for CRM 2011 uses XML. When the CRM 2011 library was created Dynamics CRM supported only Internet Explorer, including old versions like IE7. These old IE versions don't have the JSON object, so (probably) they decided to parse an XML object instead. My library in order to be compatible with these IE versions includes the JSON2 library. I can include it without issues because the first line of the library checks if JSON is already defined
    if("object"!==typeof JSON)JSON={};
    
    in this way old browsers can support JSON methods like JSON.parse.

  • getClientUrl: This Xrm method was introduced with CRM 2011 UR12, but my library in order to be compatible with pre-UR12 CRM 2011 instances checks if getClientUrl is available before using it. I used this code snippet in many of my samples in order to avoid creating a JavaScript only to handle CRM 2011 versions:
    var serverUrl;
    if (Xrm.Page.context.getClientUrl !== undefined) {
        serverUrl = Xrm.Page.context.getClientUrl();
    } else {
        serverUrl = Xrm.Page.context.getServerUrl();
        if (serverUrl.match(/\/$/)) { serverUrl = serverUrl.substring(0, serverUrl.length - 1); }
    }
    
  • response and responseText: Older versions of Internet Explorer don't implement the response property inside the object returned by the XmlHttpRequest, so if the property is not defined we will use the responseText instead.
    if (this.response !== undefined) { wrContent = this.response; }
    // handling responseText for older IE versions
    else { if (this.responseText !== undefined) { wrContent = this.responseText; } }
    
  • setTimeout: The setTimeout syntax used inside the SDK sample is not compatible with older IE versions, an easy fix is to wrap the function called inside an anonymous function:
    // original SDK sample code
    setTimeout(SDK.DependentOptionSet.filterOptions, 100, parentFieldParam, childFieldParam, dependentOptionSet);    
    // setTimeout rewritten for older IE versions
    setTimeout(function() { DO.DependentOptionSet.filterOptions(parentFieldParam, childFieldParam, dependentOptionSet); }, 100);
    
  • getClient and CRM for Tables and Phones: The SDK sample handles also the compatibility with CRM for mobile devices, but in order to handle the call to the method Xrm.Page.context.client.getClient() it's necessary to check if the context object contains the client property and its getClient method:
    if (Xrm.Page.context.client !== undefined && Xrm.Page.context.client.getClient !== undefined) {
        // ... rest of the code
    }
    
  • forEach: The SDK sample uses forEach to iterate the available values for the child OptionSet, but forEach is not available for older IE versions, the code has been rewritten to use a standard for cycle:
    //original SDK sample code
    validOptionValues.forEach(function (optionValue) { /* ... */ })
    // standard for cycle
    for (var count = 0; count < validOptionValues.length; count++) { /* ... */ }
    
Making the library compatible with CRM 2011 was more an exercise for me, CRM 2011 is officially not supported and I hope that many instances have been already migrated to newer versions, but I am aware that this is not always possible.

However the main part of my solution is the Web Application to create/update the JSON configuration, if you are already using the CRM 2016 SDK Sample in your CRM, you can use the Web Application without using my library and just update the Web Resources.