8

Due to complex requests provided by customers, sometimes my code get's messy. I take time to read and understand what I've written since I last worked on it but it takes time.

I was wondering if anyone has implemented a good design pattern which saves time and makes code more organized and readable etc.

Middletone
  • 4,190
  • 12
  • 53
  • 74
Anwar
  • 4,470
  • 4
  • 24
  • 30

3 Answers3

9

Having a base plugin that implements IPlugin is a good step in the right direction. It's Execute function could pass in the IServiceProvider as well as your dataContext or OrganizationService into an abstract onExecute method, which is wrapped in a try catch with a virtual error handler method. This would eliminate a lot of repetitive boilerplate code...

EDIT 1

Added code example showing an abstract OnExecute and a virtual error handler:

public abstract class PluginBase : IPlugin
{

    public void Execute(IServiceProvider serviceProvider)
    {
        try
        {
            OnExecute(serviceProvider);
        }
        catch (Exception ex)
        {
            bool rethrow = false;
            try
            {
                OnError(ex);
            }
            catch
            {
                rethrow = true;
            }

            if (rethrow)
            {
                throw;
            }
        }
        finally
        {
            OnCleanup();
        }
    }

    // method is marked as abstract, all inheriting class must implement it
    protected abstract void OnExecute(IServiceProvider serviceProvider);

    // method is virtual so if an inheriting class wishes to do something different, they can
    protected virtual void OnError(Exception ex){
        // Perform logging how ever you log:
        Logger.Write(ex);
    }

    /// <summary>
    /// Cleanup resources.
    /// </summary>
    protected virtual void OnCleanup()
    {
        // Allows inheriting class to perform any cleaup after the plugin has executed and any exceptions have been handled
    }
}

Edit 2

I have a plugin base defined in DLaB.Xrm (on Nuget) in the DLaB.Xrm.Plugin Namespace that allows handles a lot of great things for you. Here is an example plugin class showing you how to use it.

Daryl
  • 18,592
  • 9
  • 78
  • 145
  • Daryl, could you please illustrate your idea showing your code because I don't get "abstract onExecute method" and the "virtual error handler". – Anwar Jun 19 '12 at 19:41
  • 2
    Generally agree with the BasePlugin idea. It allows you to reduce repetitive code e.g. SetState requests, Association requests that kind of thing. You might even include an ObjectTypeCode enumeration. Does rather depend on what you requirements are (how many plugins? What are they doing?). Also consider your target install location - extra DLLs have to be installed in your server GAC if installing plugins to the DB. – glosrob Jun 19 '12 at 20:24
  • Please find below updated link: https://github.com/daryllabar/XrmUnitTest/blob/f5971ab42a7c129d55f56d7959eed776a0b0dd9e/Example/Xyz.Xrm.Plugin/Simple/RemovePhoneNumberFormatting.cs – Dot_NET Pro Aug 16 '18 at 05:05
3

I work based on 2 classes:

The first one called PluginContext:

using System;

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;

/// <summary>
/// The plugin context.
/// </summary>
public class PluginContext
{
    #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="PluginContext"/> class.
        /// </summary>
        /// <param name="serviceProvider">
        /// The service provider.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public PluginContext(IServiceProvider serviceProvider)
        {
            if (serviceProvider == null)
            {
                throw new ArgumentNullException("serviceProvider");
            }

            // Obtain the execution context service from the service provider.
            this.PluginExecutionContext =
                (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

            // Obtain the tracing service from the service provider.
            this.TracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            // Obtain the Organization Service factory service from the service provider
            var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

            // Use the factory to generate the Organization Service.
            this.OrganizationService = factory.CreateOrganizationService(this.PluginExecutionContext.UserId);

            this.OrganizationServiceContext = new OrganizationServiceContext(this.OrganizationService);
        }

        #endregion

    #region Properties

        /// <summary>
        /// Gets the organization service.
        /// </summary>
        /// <value>
        /// The organization service.
        /// </value>
        public IOrganizationService OrganizationService { get; private set; }

        /// <summary>
        /// Gets the plugin execution context.
        /// </summary>
        /// <value>
        /// The plugin execution context.
        /// </value>
        public IPluginExecutionContext PluginExecutionContext { get; private set; }

        /// <summary>
        /// Gets the service provider.
        /// </summary>
        /// <value>
        /// The service provider.
        /// </value>
        public IServiceProvider ServiceProvider { get; private set; }

        /// <summary>
        /// Gets the tracing service.
        /// </summary>
        /// <value>
        /// The tracing service.
        /// </value>
        public ITracingService TracingService { get; private set; }

        /// <summary>
        /// Gets the organization service context.
        /// </summary>
        /// <value>
        /// The organization service context.
        /// </value>
        public OrganizationServiceContext OrganizationServiceContext { get; private set; }

        #endregion

    #region Methods

        /// <summary>
        /// The trace.
        /// </summary>
        /// <param name="message">
        /// The message.
        /// </param>
        public void Trace(string message)
        {
            if (string.IsNullOrWhiteSpace(message) || this.TracingService == null)
            {
                return;
            }

            if (this.PluginExecutionContext == null)
            {
                this.TracingService.Trace(message);
            }
            else
            {
                this.TracingService.Trace(
                    "{0}, Correlation Id: {1}, Initiating User: {2}", 
                    message, 
                    this.PluginExecutionContext.CorrelationId, 
                    this.PluginExecutionContext.InitiatingUserId);
            }
        }

        #endregion
}

The second one called PluginBase:

using System;

using Microsoft.Xrm.Sdk;

/// <summary>
///     Base class for all Plugins.
/// </summary>
public abstract class PluginBase : IPlugin
{
    #region Public Properties

    /// <summary>
    ///     Gets the entity reference.
    /// </summary>
    /// <value>
    ///     The entity reference.
    /// </value>    
    public EntityReference EntityReference { get; private set; }

    /// <summary>
    ///     Gets the plugin context.
    /// </summary>
    /// <value>
    ///     The plugin context.
    /// </value>    
    public PluginContext LocalContext { get; private set; }

    #endregion

    #region Properties

    /// <summary>
    ///     Gets a value indicating whether [ignore plugin].
    /// </summary>
    /// <value>
    ///     <c>true</c> if [ignore plugin]; otherwise, <c>false</c>.
    /// </value>
    /// <created>1/5/2014</created>
    protected virtual bool IgnorePlugin
    {
        get
        {
            return false;
        }
    }

    #endregion

    #region Public Methods and Operators

    /// <summary>
    /// Executes the specified service provider.
    /// </summary>
    /// <param name="serviceProvider">
    /// The service provider.
    /// </param>    
    public void Execute(IServiceProvider serviceProvider)
    {
        if (this.IgnorePlugin)
        {
            return;
        }

        this.LocalContext = new PluginContext(serviceProvider);

        if (!this.LocalContext.PluginExecutionContext.InputParameters.Contains("Target"))
        {
            return;
        }

        var entity = this.LocalContext.PluginExecutionContext.InputParameters["Target"] as Entity;
        if (entity != null)
        {
            this.EntityReference = entity.ToEntityReference();
        }
        else
        {
            this.EntityReference =
                this.LocalContext.PluginExecutionContext.InputParameters["Target"] as EntityReference;
            if (this.EntityReference == null)
            {
                return;
            }
        }

        this.Execute();
    }

    /// <summary>
    ///     Executes this instance.
    /// </summary>    
    public abstract void Execute();

    #endregion
}

The the plugin class would be like:

public class SamplePlugin : PluginBase
{
    // If don't want the plugin to be executed then override the IgnorePlugin property

    protected override bool IgnorePlugin
    {
        get
        {
            return true;
        }
    }

    public override void Execute()
    {
        var query = LocalContext
            .OrganizationServiceContext
            .CreateQuery("account")
            .Where(a => (string)a["accountname"] == "test accounts")
            .ToList();
    }
}
Ahmed Magdy
  • 5,956
  • 8
  • 43
  • 75
  • I use a similar concept - i've implemented quite a bit more logic as my framework progressed. one of which was adding a "trace log" -> a list of trace calls and when debugging is set to true as a flag some where else - it'll throw a joined list of the traces it received during the execution of the plugin. – Travis Sharp Apr 10 '14 at 19:44
2

i know i maybe late but ...

there are several plugin templates for visual studio out there, and those samples will give you the idea of what's best.

One of my favourites is: http://pogo69.wordpress.com/2011/04/15/crm-2011-visual-studio-plugin-templates/

Anyway, you must know that eventually your logic design will improve and get better, all you need is practice.

Reggards!

Sebas Tian
  • 65
  • 6