How to Do Dependency Injection in a CRM Plugin
I first approached this because I thought it was ridiculous how our current mega-Plugins that handle all of our integrations were impossible to debug. I needed a way to separate the logic from the instantiation of the plugins inside of CRM. CRM introduces a ton of constraints as far as being able to code cleanly and take advantage of patterns that make the code maintainable and debuggable.
I usually use either the constructor or property injection patterns, as needed, from “Dependency Injection in .NET” by Mark Seemann http://www.manning.com/seemann/. So the first thing I did was separate out a logic layer from the Plugin. I couldn’t use constructor injection directly in the parent Plugin class because CRM caches the instantiation of Plugin classes to be used amongst the pool of messages triggering the plugin for performance.
Public Sub Execute(ByVal context As IPluginExecutionContext) Implements IPlugin.Execute
If context.Depth > 1 Then
Return
End If
Dim logWriter As ILogWriter = New LogWriter("...", "ON")
Dim roiService As IROIService = New ROIService(_IsCRMTestServer)
Dim crmService As ICrmService = context.CreateCrmService(True)
Dim quoteStateUtils As IQuoteStateUtilities = New QuoteStateUtilities(crmService, logWriter)
logWriter.WriteLine("*****************************************************************")
logWriter.WriteLine("This plugin is on the " + IIf(_IsCRMTestServer = True, "test", "live") + " CRM server.")
// CONSTRUCTOR DI...
// THIS PREVENTS INSTANCE CACHING ISSUE IN CRM
Dim quoteStateLogic As QuoteStateLogic = New QuoteStateLogic(logWriter, quoteStateUtils, roiService, crmService)
quoteStateLogic.Execute(context)
End Sub
Public Class QuoteStateLogic
Private ReadOnly _logWriter As ILogWriter
Private ReadOnly _roiService As IROIService
Private ReadOnly _crmService As ICrmService
Private ReadOnly _quoteStateUtils As IQuoteStateUtilities
Public Sub New(logWriter As ILogWriter, quoteStateUtils As IQuoteStateUtilities, roiService As IROIService, crmService As ICrmService)
Me._logWriter = logWriter
Me._quoteStateUtils = quoteStateUtils
Me._roiService = roiService
Me._crmService = crmService
End Sub
Public Sub Execute(ByVal context As IPluginExecutionContext) {
...
With this, I’ve achieved the end goal, which was to remove the plugin logic’s dependency on concrete implementations. So the fact that the main Plugin.Execute() is now the composition root for the QuoteStateLogic class, I have complete control over the Plugin’s dependencies and I have taken away the responsibility of the logic-only class to instantiate its own dependencies. I was able to write a huge suite of unit tests and ignore my integration tests unless I absolutely needed them. I was also able to generate XML test fixtures using the CrmService’s Entity-serialization constructs.