Slim Solution, a Plugin for XrmToolbox

Recently, I was given many unmanaged Dynamics 365 solutions to maintain. The thing I hate about solution is that they can become messy with a click of a button when you add an existing component to it. By Messy I mean there are a lot of things that are not needed but added to the solution. If you add the Case entity, many developers do add the whole case entity even though one or 2 fields are needed to be modified, the rest of the information is confusing and it is not very straight forward to clean that up.

The problem gets worse when you want to build a managed solution out of the unmanaged one. The managed solution needs to be very clear on what it does to what parts of the system. If the managed solution changes one case field, adds a new relationship then those the only changes that need to exist in the solution.

While cleaning out the solutions manually, by looking at their managed exports (you can know if a component has changed by looking at its managed XML export). I decided to write a very small XrmToolbox plugin that helps me in that. I wrote the plugin sometime ago but it took a while to validate it as XrmToolbox has a new lengthy validation process.

The basic idea of the plugin is that it checks all the managed entities that are added into the solution, find which field, form or view is either customized or added to that managed entity. Then it will tell you which components need to be in the solution so that you can remove the rest.

The plugin is called SlimSolution and it is currently available for download in the XrmToolbox. It is still lacking many of the features I want such as checking other component types but I will be adding those in the near future.

An example of the usage of this plugin would be something like this. You create a solution (or other developers do) and you want to clean it up from the unwanted components. As an example, I created the below solution that has:

  1. A custom unmanaged entity
  2. 3 managed entities in which I did add all components to them and metadata.
  3. I modified/added some fields in the account and KB article entities and did nothing to the Agreement entity.

What I want is to clean up this solution by only keeping the managed entities that have been customized.

When you open the SlimSolution plugin, you first load the solutions and hit Check Solution. A somewhat nice summary appears on the right with some details and suggestions on what are the changes that need to stay in those managed entities. Of course and as I mentioned above, the plugin only checks for Forms, Views and Fields for now and gives you the list of components that need to stay in the solution.

You can see that the unmanaged entity is not mentioned because the plugin assumes unmanaged entities are created to be included in the solution (not always the case though but this is the assumption here). You will see information about what needs to stay from the account and KB entities because they were modified. You don’t see anything for the agreement entity which means that whole entity can be removed from the solution.

In addition to the above, if the solution contains some inactive processes/BPFs/Dialogs, it will alert you to remove them from the solution. The code for this plugin is constructed in away that makes adding component validators an easy task which I will do in the near future as I have some other validator ideas in mind.

Tips on Dynamics 365 Plugin Code validation for AppSource Submissions

Not long ago, I was involved in submitting a really complex application built on top of Dynamics 365 to Microsoft AppSource. The application contains a lot of plugins and code activities that perform some complex tasks and automation. The team faced some issues that I think are worth sharing with others to save your time if you you are working on such a submission.

Microsoft, provides us with tools such as the Solution Checker that validates your solution including your plugin and web resource code. The problem is, that’s not all. When you submit an application to the AppSource team, it goes through a rigorous manual and automatic checks using tools that are not publicly available to us, developers. If there are issues in your code, your submission will be rejected with explanation on what to fix and with the list of issues ordered based on their priorities. To pass the submission, all critical and high priority issues need to be fixed (if you can convince the AppSource team that somethings needs to be done a certain way and can’t be done another way, they will mostly make an exception).

After the first submission, the app got rejected with tons of things to modify/fix (even after running the solution checker on all the solutions). To be honest, the documents they sent were scary (1000+) pages with explanations on the issues. After looking at the issue list, it turned out that 90% of the critical/high priority issues are related to writing thread safe plugins. Luckily, the fix was very easy for those issues but it cost us around 2 weeks of time to do another submission and get it verified again. The following are the most common critical issues.

Variables May Cause Threading Issues

A plugin in Dynamics, is a simple class that implements the IPlugin interface, and thus, has a single Execute method as a minimum. Almost always, you need to create the organization service, the tracing service, the context and maybe other object. A bare bone plugin that builds, will look something like this:

public class SomePlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        throw new NotImplementedException();
    }
}

A useful plugin, will have extra objects created so that we can communicate with the Dynamics organization,

public class SomePlugin: IPlugin {
 // Obtain the tracing service
 ITracingService tracingService = null;
 IPluginExecutionContext context = null;
 // Obtain the execution context from the service provider.  

 public void Execute(IServiceProvider serviceProvider) {
  tracingService =
   (ITracingService) serviceProvider.GetService(typeof(ITracingService));

  context = (IPluginExecutionContext)
  serviceProvider.GetService(typeof(IPluginExecutionContext));
 }
}

Now what’s wrong with the above plugin code? In a normal .NET application, this is a normal thing to do, but in a Dynamics plugin, it is not. To understand why, we need to understand how plugins get executed on our behalf behind he scenes. When a plugin runs for the first time (because of some trigger), most of the plugin global variables get cached, this happens when the constructor of the plugin is first executed. This means, in the next run, the same tracing service and context “may” be shared with the next run. This applies on any variable you define outside your function as a global variable in your plugin class. Ultimately, this causes threading issues (multiple runs of the same plugin instance compete for the same cached variable) and you may end up with extremely difficult-to-debug errors and unexplained deadlocks. The fix for the above, is very simple, just create your variables locally in the execute function, so each run of the plugin executes its own set of local variables.

public class SomePlugin: IPlugin {
 public void Execute(IServiceProvider serviceProvider) {
 ITracingService  tracingService =
   (ITracingService) serviceProvider.GetService(typeof(ITracingService));

  IPluginExecutionContext context = (IPluginExecutionContext)
  serviceProvider.GetService(typeof(IPluginExecutionContext));
 }
}

This by default means, that any helper function in your plugin should get what it needs from its parameters and not from global variables. Assume you have a function that needs the tracing service, and this function get’s called from the Execute method, pass the tracing service that was created in the execute method to that function and don’t make it a global object.

public class SomePlugin: IPlugin {
 public void Execute(IServiceProvider serviceProvider) {
 ITracingService  tracingService =
   (ITracingService) serviceProvider.GetService(typeof(ITracingService));

  IPluginExecutionContext context = (IPluginExecutionContext)
  serviceProvider.GetService(typeof(IPluginExecutionContext));
// do work here
HelperFunction(tracingService,1,2,"string");
 }

private void HelperFunction(ITracingService tracingService, int param1, int param2, string param3)
{
//use tracing service here
}
}

On the other hand, anything that is read only (config string, some constant number) is safe to stay as a global class member.

Plugins That Trigger on Any Change

This problem is more common. The filtering attributes of a plugin, are a way to limit when that plugin executes. Try to have as few as possible of those filtering attributes, don’t specify all of them. At that time I was involved in that submission, the solution checker wasn’t able to detect such problem but it may have improved now.

Image result for filtering attributes

Plugins That Update the Record Or Retrieve Attributes Again

This is also a common issue, when a plugin is triggered on an update of an entity record, it is really a bad idea to issue another update request to the same record again. An example of this can be the need to update fieldX based on the value of fieldY. When the plugin triggers on fieldY change, you issue an service.Update(entity) with the new value of fieldX. This implicates the performance of the whole organization and even worse, it can cause an infinite loop if the filtering attributes are not set properly. Another, bad use case is to issue a retrieve attributes query for the same record when pre-images and post images can be used to remedy that.

To be clear, sometimes, there is no way around issuing another retrieve inside the plugin or sending a self-update request, we had some of those cases and we were able to convince the AppSource team that our way was the only way.

Slow Plugins

As a general rule of thumb, your plugin should be slim and does a very small thing and does it fast. Plugins have some upper limit on the time they can run within and your plugin should never exceed that time (or not even half of it). When your plugin does exceed the time allocated for it, it is time for redesigning it.

Conclusion

While those issues have simple fixes in general, they can cause slowness and unexplained errors and a rejection from AppSource. Even if you are not submitting anything to AppSource, make sure that you set some ground rules for the developers working on the same code base on how to write good plugins. More on plugins best practices can be found here.

What this blog is about?

Digitally transforming your business is a daunting task. It requires revamping the whole organization processes and systems to meet the never ending demands of today’s digital world. We all face problems with this kind of transformation, and since many of those problems are technical, we would love to help!

We are a group of professionals working with the latest technologies in the Business Intelligence, Business Applications, Cloud Computing, Data Science, Machine Learning and Software Development fields. Our goal is to help the community by providing solutions to problems we face in our day to day jobs that we think other people may face too.