Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency Injection in MS Dynamics CRM

I am currently getting started with the extending of Microsoft Dynamics CRM using Plugins.

Is it possible to add Dependency injection to these plugins (for testing, loose coupling, etc. purposes)? Where can I register my IoC-container so that it's used over all the plugins of the same type?

like image 799
Jente Rosseel Avatar asked Jan 15 '14 14:01

Jente Rosseel


2 Answers

We've been trying to unit test and apply dependency injection on our Dynamics CRM application. Unfortunately, as Microsoft support and consultants are confirmed, there is no supported way to do it. You may either transfer all of your plugin business logic to an another business class and apply dependency injection or stop thinking about it.

If you choose to fight back with Dynamics CRM, you need to define a static field on a plugin super class which will be your DI Container. As follows,

public abstract class SuperPlugin : IPlugin{
       public void Execute(IServiceProvider serviceProvider){
            // initialize a static container instance if not available
            var containerWrapper = new ContainerWrapper{
                Container = serviceProvider.GetService(typeof(IPluginExecutionContext)),
                Resolver = //static resolver instance of dependency container
            };
            OnExecution(containerWrapper);
       }
       public abstract void OnExecution(IDependencyResolver resolver);
}

I really cannot understand why Microsoft doesn't simply let us register some components to the IServiceProvider implementation that they are using internally.

Ps. Since your SuperPlugin class is an IPlugin, you may forget to write the interface implementation on the sub class. But we encountered some bugs on Plugin Registration tool that is shipped with official Dynamics CRM SDK. So in case you may have the same problem you should also implement your plugins as follows,

public class MyPlugin : SuperPlugin, IPlugin{
  public abstract void OnExecution(IDependencyResolver resolver){};
}

Edit: See a small example that explains the concept https://github.com/nakahparis/DIForCRM

like image 175
Mert Susur Avatar answered Sep 21 '22 18:09

Mert Susur


Plugins in CRM are the Bane of Unit Testing:

  • Issues with non-plugin test
    • No way to temporarily disable
    • Easy to forget it is running
  • Issues with testing plugins themselves
    • Unable to unit test and attach to process
    • A lot to mock out, Pipeline, Service Provider etc
    • Runs multi-threaded

This has led me to the following solution for testing plugins:

  • Get rid of the plugin context as quickly as possible, extracting out all objects and service required from it right away.
  • Create an ExecutePlugin method to hook unit tests into, and immediately call this method after extracting the objects from the plugin context.
  • Push as much code as possible into the business layer.

This results in plugins that look like this (with a heavy use of extension methods to make this simpler):

public void Execute(IServiceProvider provider)
{
    var context = provider.GetContext();
    var service = provider.GetService(context);
    var target = GetTarget<Contact>(context);
    if (target == null || !target.ContainsAllNonNull(c => new
        {
            c.FirstName,
            c.LastName,
        }))
    {
        // Entity is of the wrong type, or doesn't contain all of the required attributes
        return;
    }

    ExecutePlugin(service, target);
}

public void ExecutePlugin(IOrganizationService service, Contact target){
    // Logic Goes Here
}

Once this is done, the only thing you need to unit test the ExceutePlugin is your own IOrganizationService that mocks out the required calls and you have your unit testing done. I don't even bother unit testing the Execute method. Either it'll work, or it won't and blow chow on the first use from within CRM.

like image 38
Daryl Avatar answered Sep 21 '22 18:09

Daryl