Tag: Extension Methods

Using Extension Methods in Dynamics 365 CE Plugins

In my previous blog post, Extension Methods in the .NET Framework, I covered how to use extension methods in C# and VisualBasic to extend the functionality of base types. Using extension methods can also be useful when writing plugins for Dynamics 365 Customer Engagement (Dynamics CRM). I create my own library of extension methods that can be used as needed for any project I’m currently working on. The most common types of extensions for me involves the tracing service, the organization service, and the entity.

I have two types of tracing that I perform. The first is standard trace messages that are always logged to the plugin trace logs. The second is a debug-only trace message that only writes to the plugin trace log if the assembly is compiled in Debug as opposed to Release. As a side comment, it is always preferable to have your assemblies compiled in Release mode when deployed to a production ready environment, to improve plugin performance. This makes using the #if DEBUG preprocessing directive more effective.

Without an extension message, sending a message to the plugin tracing log is done by calling the Trace method of Microsoft.Xrm.Sdk.ITracingService.

using Microsoft.Xrm.Sdk;

public namespace Sample.Tracing
{
    public class PluginTest : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            tracingService.Trace("Entering plug-in execution.");
            tracingService.Trace("Some debug information is logged to the plugin trace log.");
            // Additional code goes here
        }
    }
}

The standard way of tracing is effective on its own but is still missing some elements that make it useful. As mentioned previously, you may only want certain tracing calls to be done if you are actively debugging your code. Prepending every call to the trace log with a time stamp is also beneficial when trying to determine where plug-in performance is being adversely impacted. You can also parse your error object to output more detailed error messages directly to the plugin trace logs. For the sample below, I’m just going to demonstrate how we can expand the tracing log to handle debug only messages and time-stamped messages.

using Microsoft.Xrm.Sdk;
using System;

namespace Sample.SdkExtensions
{
    public static class ExtITracingService
    {
        public static void DebugMessage(this ITracingService tracingService, string format, params object[] args)
#if DEBUG
            => tracingService.LogMessage(format, args);
#else
            { }
#endif
    }

    public static void LogMessage(this ITracingService tracingService, string format, params object[] args)
    {
        string message = (args == null || args.Length == 0)
            ? format
            : String.Format(format, args);
        tracingService.Trace("{0}: {1}", DateTime.Now.ToString("o"), message);
    }
}

The DebugMessage extension method shows how to use the preprocessing directive to only execute when running in Debug mode. If the code is running in Debug, then it will call the LogMessage extension method. If it isn’t, then the method doesn’t do anything at all. This will allow us to have more detailed messages when running the assembly in Debug, and better performance when running the assembly in Release.┬áThe LogMessage extension method will create the appropriate message string from the format and optional arguments and pass that directly over to the ITracingService, prepended with a date and time stamp.

Now, we can update the plugin code to use our new extension methods.

using Sample.SdkExtensions;
using Microsoft.Xrm.Sdk;

public namespace Sample.Tracing
{
    public class PluginTest : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            tracingService.LogMessage("Entering plug-in execution.");
            tracingService.DebugMessage("Some debug information is logged to the plugin trace log.");
            // Additional code goes here
        }
    }
}

As you can see, once we have our extension method in the project, we can reference the namespace for it in our using statements. At that point, our DebugMessage and LogMessage extension methods are able to be used. Now, debug messages will only be added to the plugin tracing log if the assembly is compiled as Debug, and both messages will be prepended with the appropriate time stamp. These extension methods increase the readability and usability of the code, giving consistent logging functionality no matter where the calls are made in the code.

Extending the entity is another area that it is useful to create extension methods. Getting and setting of attribute values often requires the same several lines of code to retrieve the appropriate information. Examine the code below using the standard SDK calls to retrieve the primary contact Id from one account, and set it on another. While it is preferable to retrieve the Entity Reference in this type of scenario, I am writing in this fashion to show the value in reducing code complexity.

Guid entityReferenceId = account1.Contains("primarycontact")
    ? ((EntityReference)account1["primarycontact"]).Id
    : Guid.Empty;

if (entityReferenceId == Guid.Empty)
{
    if (account2.Contains("primarycontact"))
    {
        account2["primarycontact"] = null;
    }
}
else
{
    if (account2.Contains("primarycontact"))
    {
        account2["primarycontact"] = new EntityReference("contact", entityReferenceId);
    }
    else
    {
        account2.Attributes.Add("primarycontact") = new EntityReference("contact", entityReferenceId);
    }
}

There are several lines of code required to perform a simple get and set operation. These operations can be created as extension methods on Microsoft.Xrm.Sdk.Entity as we did for the tracing service. I won’t be putting the extension methods here, instead, I will show how we can use two new extension methods GetEntityReferenceId and SetEntityReference to increase the readability of the plugin code.

Guid entityReferenceId = account1.GetEntityReferenceId("primarycontact");
account2.SetEntityReference("contact", entityReferenceId);

As you can see, the extension methods now make it easier to set and get values from the entity image. As mentioned previously, this increases readability and reusability of the code, making debugging easier. Just be aware that if you are setting default values (such as Guid.Empty for Guid values) you’ll want to have additional checks in your code to ensure that you are working with the proper data.

The final extension method I will touch on in this entry is related to Microsoft.Xrm.Sdk.IOrganizationService. In my previous blog post, I mentioned that it is strongly discouraged from overloading the methods of existing types. In the case of extending the organization service, I am intentionally ignoring that advice. In this case, my extension methods are simply shortcuts that are passed to the primary call, and even if Microsoft later adds their own overloads that mirror what I have done, it won’t break my existing functionality. This is an exception to the recommendation that still needs to be handled with care.

// Global Values
EntityReference entityReference = new EntityReference("account", Guid.Parse("608DA112-2681-4967-B30E-A4132321010A")); // Normally will be retrieved from an entity.
string[] columnNames= new string[] { ..list of fields.. };

// Default SDK Retrieve call.
service.Retrieve(entityReference.LogicalName, entityReference.Id, new ColumnSet(columnNames));

// Overloaded extension Retrieve call.
service.Retrieve(entityReference, columnNames);

// The overload in from the extension static class
public static Entity Retrieve(this IOrganizationService service, EntityReference entityReference, string[] columnNames)
    => service.Retrieve(entityReference.LogicalName, entityReference.Id, new ColumnSet(columnNames));

As you can see, the overload is simply calling the SDK Retrieve method with the appropriate parameter values. No additional validation is being done. It simply allows for a shorter method call to simplify the code.

As mentioned in the previous article, there is no consensus on how often to use extension methods. Microsoft does recommend using “sparingly and only when you have to.” It could be argued that the SDK methods are simple enough and don’t fall under “when you have to.” In my case, I prefer having multiple overloads, as I find that it makes debugging the code easier. My methods become shorter and easier to read. I have additional tracing methods, beyond what is shown in this blog post, to give more details in the plugin trace log. The ultimate goal for me is the maintainability, reusability, and readability of my code. Using extension methods with the SDK achieves that for me.

Extension Methods in the .NET Framework

Extension methods were first introduced in version 3.5 of the .NET Framework. They allow for easily extending a type without having to recompile or modify the original type. This is useful for extending types of which you don’t own the source code. By implementing an extension method in your code, you can call the method as if it is a native method for the type.

There are differing opinions on when to use extension methods as opposed to other coding styles. Microsoft’s recommended general guidelines state: “We recommend that you implement extension methods sparingly and only when you have to. Whenever possible, client code that must extend an existing type should do so by creating a new type derived from the existing type… For a class library that you implemented, you shouldn’t use extension methods to avoid incrementing the version number of an assembly. If you want to add significant functionality to a library for which you own the source code, you should follow the standard .NET Framework guidelines for assembly versioning.”

Inheritance, as recommended by Microsoft, is always a preferred method for extending and building upon base type functionality. Let’s say you have a base type named Animal. Animal has two methods: Walk() and Sleep(). Now, you have a dog, which is an animal, and you want that dog to bark. You don’t want that method to be on Animal, as not all animals bark. Instead, you will inherit the Animal type and create a new method called Bark(). What if you want your animal to eat? All animals eat, so you don’t want to inherit from Animal, instead, you want to modify the base type to add the extension method of Eat(). By doing this, your derived type of Dog, and any other derived type can use the newly created Eat extension method. Note that some classes are marked as sealed (NotInheritable in VB), meaning you cannot inherit to generate a new class, so extension methods can be used to extend the existing sealed functionality.

Extension methods cannot be used to override an existing method that has the exact same signature. For example, every type has a ToString() method. If you create an extension method of ToString(), the extension method will not be called, but the base type’s method will be called instead. On the other hand, if you create an extension method called ToString(string), your extension method will be called through the overloaded method, provided there isn’t another overloaded method with the same signature.

Another thing to be aware of when implementing extension methods on existing types is the possibility that other extensions can be written against the same types and have the same signature. This will result in a compile error stating that the call is ambiguous. This issue was encountered by the developers of NDepend and written about in their blog post “A problem with extension methods.” Similarly, if the base type is later updated to include a method of the same signature, your extension method will no longer be called. Using the Animal example earlier, if the base type was updated to include the missing Eat() method, then the custom extension method of Eat() will no longer be called, and errors may occur.

All types in the .NET Framework inherit from System.Object. For this reason, it is strongly advised not to add extension methods to System.Object. Adding an extension to System.Object could have inconsistent behavior and increase the possibility of errors when an existing type method is called instead of the extension method. For the same reason, it is strongly advised not to overload an existing method with a different signature, such as the ToString(string) example described previously.

Extension methods are implemented in static classes in C# or in Modules in VB. Inside the class in C#, you will create a static method that contains a first parameter of the type being extended, prefixed with the this modifier. Inside the Module in VB, you will add the method definition attribute of <extension()> to the Sub as well as include the type as the first parameter. The Extension() attribute requires that the VB file Imports System.Runtime.CompilerServices. One additional difference between C# and VB is that VB allows you to pass your type ByRef or ByVal, where C# passes as a val only. Despite this, it is not recommended to pass types ByRef, to reduce code complexity and the chance for errors.</extension()>

The following example, from Microsoft Docs C# Programming Guide, implements an extension method that will count the number of words contained in a String.

using System.Linq;
using System.Text;
using System;

namespace CustomExtensions
{
    // Extension methods must be defined in a static class.
    public static class StringExtension
    {
        // This is the extension method.
        // The first parameter takes the "this" modifier
        // and specifies the type for which the method is defined.
        public static int WordCount(this String str)
        {
            return str.Split(new char[] {' ', '.','?'}, StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }
}

namespace Extension_Methods_Simple
{
    // Import the extension method namespace.
    using CustomExtensions;
    class Program
    {
        static void Main(string[] args)
        {
            string s = "The quick brown fox jumped over the lazy dog.";
            // Call the method as if it were an 
            // instance method on the type. Note that the first
            // parameter is not specified by the calling code.
            int i = s.WordCount();
            System.Console.WriteLine("Word count of s is {0}", i);
        }
    }
}

The example below, from Microsoft Docs VB Programming Guide, implements an extension that will print the output to a console line and append the appropriate punctuation.

' Declarations will typically be in a separate module.  
Imports System.Runtime.CompilerServices  
  
Module StringExtensions  
    <Extension()>   
    Public Sub PrintAndPunctuate(ByVal aString As String,   
                                 ByVal punc As String)  
        Console.WriteLine(aString & punc)  
    End Sub  
  
End Module  

' Import the module that holds the extension method you want to use,   
' and call it.  
  
Imports ConsoleApplication2.StringExtensions  
  
Module Module1  
  
    Sub Main()  
        Dim example = "Hello"  
        example.PrintAndPunctuate("?")  
        example.PrintAndPunctuate("!!!!")  
    End Sub  
  
End Module