8
votes

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.

3
What part about the code do you believe becomes most unorganized / unreadable? - Daryl

3 Answers

9
votes

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.

3
votes

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();
    }
}
2
votes

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!