I have created the two following abstract classes that I use in my plugins and workflows:
/// <summary>
/// Base plugin class. Provides access to most often used Xrm resources.
/// </summary>
public abstract class BasePlugin : IPlugin
{
public IServiceProvider ServiceProvider { get; set; }
public ITracingService TracingService { get; set; }
public IPluginExecutionContext PluginContext { get; set; }
public IOrganizationService Service { get; set; }
public void Execute(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
TracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService));
PluginContext = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
Service = serviceFactory.CreateOrganizationService(PluginContext.UserId);
ExecutePluginLogic();
}
public virtual void ExecutePluginLogic()
{
throw new NotImplementedException();
}
}
And
/// <summary>
/// Base workflow class. Provides access to most often used Xrm resources.
/// </summary>
public abstract class BaseWorkflow : CodeActivity
{
public CodeActivityContext CodeActivityContext { get; set; }
public IWorkflowContext WorkflowContext { get; set; }
public ITracingService TracingService { get; set; }
public IOrganizationService Service { get; set; }
protected override void Execute(CodeActivityContext context)
{
IOrganizationServiceFactory serviceFactory = context.GetExtension<IOrganizationServiceFactory>();
CodeActivityContext = context;
TracingService = context.GetExtension<ITracingService>();
WorkflowContext = context.GetExtension<IWorkflowContext>();
Service = serviceFactory.CreateOrganizationService(WorkflowContext.UserId);
ExecuteWorkflowLogic();
}
public virtual void ExecuteWorkflowLogic()
{
throw new NotImplementedException();
}
}
Here's how I would create a plugin then:
public class CalculateTaxesOnUpdate : BasePlugin
{
public override void ExecutePluginLogic()
{
//From there I don't need to instanciate anything...neat!
}
}
This seems to work fine and helps to reduce boiler plate code when it comes to initiating instances of IOrganizationService
and ITracingService
namely.
But I've noticed that on some messages (ie: Update
of invoicedetail
) that are triggered with short delays, on the first execution, public properties of BasePlugin
are null
(which is expected) then on the following executions, they are already initiated (??). I noticed this being an issue because I had a Dispose
method in the base classes which would set properties to null after executing ExecutePluginLogic
and other threads would then try to use null
properties.
Since I'm not reusing them and re-initiating them anyways (which is what happens when you instanciate everything in Execute
anyways), I don't know if that's an issue, but am I going against best practices here ?
A class which contains one or more abstract methods is called an abstract class. An abstract method is a method that has a declaration but does not have an implementation.
Your Own Custom Abstract WordPress Plugin In the end, the best choice for abstraction will always be to create your plugin. It may seem like a big undertaking, but if you have limited ability to manage your WordPress core files directly, this offers an abstraction-friendly workaround.
Abstract classes are very similar to interfaces but they have different rules that a web developer need to be aware of. The definition is quite straightforward, basically, an abstract class is a class that contains abstract methods, Abstract methods are methods that have been declared but not implemented in the code.
Concrete classes contain only concrete (normal)methods whereas abstract classes may contain both concrete methods and abstract methods. The concrete class provides an implementation of abstract methods, the abstract base class can also provide an implementation by invoking the methods via super ().
Just because it is a base class doesn't eliminate the issue with class level variables in CRM plugins (and workflows.)
From https://msdn.microsoft.com/en-us/library/gg328263.aspx#bkmk_writingbasic:
For improved performance, Microsoft Dynamics CRM caches plug-in instances. The plug-in's Execute method should be written to be stateless because the constructor is not called for every invocation of the plug-in. Also, multiple system threads could execute the plug-in at the same time. All per invocation state information is stored in the context, so you should not use global variables or attempt to store any data in member variables for use during the next plug-in invocation unless that data was obtained from the configuration parameter provided to the constructor. Changes to a plug-ins registration will cause the plug-in to be re-initialized.
Having class level variables violates this stateless requirement.
My recommendation is to rewrite the plugin (and then do the same for the workflow) to have an object that holds the references for each call to Execute
, thus allowing the code to meet the stateless requirement.
public class CrmObjects
{
public IServiceProvider ServiceProvider { get; set; }
public ITracingService TracingService { get; set; }
public IPluginExecutionContext PluginContext { get; set; }
public IOrganizationService Service { get; set; }
}
public abstract class BasePlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var crmObjects = new CrmObjects();
crmObjects.ServiceProvider = serviceProvider;
crmObjects.TracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService));
crmObjects.PluginContext = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
crmObjects.Service = serviceFactory.CreateOrganizationService(crmObjects.PluginContext.UserId);
ExecutePluginLogic(crmObjects);
}
public virtual void ExecutePluginLogic(CrmObjects crmObjects)
{
throw new NotImplementedException();
}
}
I wrote a blog article about doing something similar, http://nicknow.net/dynamics-crm-2011-abstracting-plugin-setup/, a few years back. In the model I described it didn't rely on a base class but used a class that was instantiated on the first line of the Execute
method to accomplish the same concept. I've since moved to a base class model - similar to this design. When I get a chance I'll put it out on GitHub.
Also, even if you ever make your base class generic (maybe based on the type of context that it retrieves), you'll need to add the : IPlugin
interface to all plugins, even though the base class defines it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With