Dynamics Crm 2011 Trace Listener for Enterprise Library Logging

If you use Enterprise Library to log information and message in your program and you happen to develop Dynamics CRM related programs, then Dynamics Crm 2011 Trace Listener is a tool to log information and message to Dynamics CRM entity. If you develop systems connected to Dynamics CRM then you can use this tool to have a central place to check the end results including the log.

It is available as NuGet package: http://nuget.org/packages/EnterpriseLibrary.Logging.DynamicsCrm2011

The project site is: http://entlibmscrmlog.codeplex.com/

After installing the NuGet package, there will be a folder added to visual studio project which includes two *.zip file. One is the un-managed Dynamics CRM solution and the other is managed Dynamics CRM solution. These two solutions are the sample except one being managed and the other is not. The solution contains one single entity where all the log information and message are saved.

After import the solution into Dynamics CRM organisation, you need to create a connection string in your web.config/app.config file.

The following is a sample configuration for On Premise Integrated Windows Authentication

<pre><?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="Crm" connectionString="Url=https://domain.com/orgname;"/>
  </connectionStrings>
</configuration></pre>

More samples please see project site or http://msdn.microsoft.com/en-us/library/gg695810.aspx

After than you can configure enterprise library logging using the configuration file either manually or using enterprise library configuration tool. It also support configuration through fluent API.

Dynamics CRM 2011 Unit Test Part 14: Rhino Mocks with CRM plugin

Dynamics CRM 2011 Unit Test Part 1: Introduction and Series Contents

The complete sample code using Rhino Mocks can be downloaded from MSDN sample gallery: Dynamics CRM unit test using Rhino Mocks

Unit test CRM plugin is straightforward as well, it is just a bit tedious. IPlugin.Execute only accept one parameter which is IServiceProvider. Through IServiceProvider we can retrieve at least 3 interface and we need to set up 5 interface, which will be IOrganizationService, IPluginExecutionContext, ITracingService, IOrganizationServiceFactory and IServiceProvider.

Suppose we have a sample Dynamics CRM plugin, which is called after a contact is created within Dynamics CRM, if the parent customer is set to an account, the number of employees attribute of that account will be increased by one.

Code Under Test


public class Plugin : IPlugin
 {
 private string _unsecure, _secure;

/// <summary>
 /// Alias of the image registered for the snapshot of the
 /// primary entity's attributes after the core platform operation executes.
 /// The image contains the following attributes:
 /// parentcustomerid
 ///
 /// Note: Only synchronous post-event and asynchronous registered plug-ins
 /// have PostEntityImages populated.
 /// </summary>
 private readonly string postImageAlias = "PostCreateImage";

public Plugin(string unsecure, string secure)
 {
 _unsecure = unsecure;
 _secure = secure;
 }

public void Execute(IServiceProvider serviceProvider)
 {
 // Obtain the execution context service from the service provider.
 var pluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

if (pluginExecutionContext.Stage == 40 && pluginExecutionContext.MessageName == "Create" && pluginExecutionContext.PrimaryEntityName == "contact")
 {
 // Obtain the tracing service from the service provider.
 var tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

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

// Use the factory to generate the Organization Service.
 var organizationService = factory.CreateOrganizationService(pluginExecutionContext.UserId);

Entity postImageEntity = (pluginExecutionContext.PostEntityImages != null && pluginExecutionContext.PostEntityImages.Contains(this.postImageAlias)) ? pluginExecutionContext.PostEntityImages[this.postImageAlias] : null;

// when a contact is created with parent account, increase number of employees for that account
 if (postImageEntity != null && postImageEntity.Contains("parentcustomerid"))
 {
 EntityReference parentCustomer = postImageEntity.GetAttributeValue<EntityReference>("parentcustomerid");

if (parentCustomer != null && parentCustomer.LogicalName.ToLowerInvariant() == "account")
 {
 tracingService.Trace("Parent account id: {0}.", parentCustomer.Id);

Entity parentAccount = organizationService.Retrieve("account", parentCustomer.Id, new Microsoft.Xrm.Sdk.Query.ColumnSet("numberofemployees"));

int numberOfEmployees = 0;
 if (parentAccount.Contains("numberofemployees"))
 {
 numberOfEmployees = parentAccount.GetAttributeValue<int>("numberofemployees");
 }

parentAccount["numberofemployees"] = numberOfEmployees + 1;

organizationService.Update(parentAccount);
 }
 }
 }
 }
 }

Unit Test

[TestMethod()]
 public void ExecuteTest()
 {
 //
 // Arrange
 //
 string unsecure = "unsecure";
 string secure = "secure";
 Plugin target = new Plugin(unsecure, secure);

var accountId = Guid.NewGuid();
 var previousNumber = 3;
 var expected = 4;
 var actual = 0;

// IOrganizationService
 var service = MockRepository.GenerateStub<IOrganizationService>();
 service.Stub(x => x.Retrieve(null, Guid.Empty, null)).IgnoreArguments().Return(
 new Microsoft.Xrm.Sdk.Entity("account")
 {
 Id = accountId,
 Attributes = { { "numberofemployees", previousNumber } }
 });

service.Stub(x => x.Update(null)).IgnoreArguments().Do((Action<Entity>)delegate(Entity entity)
 {
 actual = entity.GetAttributeValue<int>("numberofemployees");
 });

// IPluginExecutionContext
 var pluginExecutionContext = MockRepository.GenerateStub<IPluginExecutionContext>();
 pluginExecutionContext.Stub(x => x.Stage).Return(40);
 pluginExecutionContext.Stub(x => x.MessageName).Return("Create");
 pluginExecutionContext.Stub(x => x.PrimaryEntityName).Return("contact");
 pluginExecutionContext.Stub(x => x.PostEntityImages).Return(new EntityImageCollection
 {
 { "PostCreateImage", new Microsoft.Xrm.Sdk.Entity("contact")
 {
 Attributes = { { "parentcustomerid", new EntityReference("account", accountId) } }
 }
 }
 });

// ITracingService
 var tracingService = MockRepository.GenerateStub<ITracingService>();
 tracingService.Stub(x => x.Trace(null, null)).IgnoreArguments().Do((Action<string, object[]>)delegate(string f, object[] o)
 {
 Debug.WriteLine(f, o);
 });

// IOrganizationServiceFactory
 var factory = MockRepository.GenerateStub<IOrganizationServiceFactory>();
 factory.Stub(x => x.CreateOrganizationService(null)).IgnoreArguments().Return(service);

// IServiceProvider
 var serviceProvider = MockRepository.GenerateStub<IServiceProvider>();
 serviceProvider.Stub(x => x.GetService(null)).IgnoreArguments().Do((Func<Type, object>)delegate(Type t)
 {
 if (t == typeof(IPluginExecutionContext))
 {
 return pluginExecutionContext;
 }
 else if (t == typeof(ITracingService))
 {
 return tracingService;
 }
 else if (t == typeof(IOrganizationServiceFactory))
 {
 return factory;
 }

return null;
 });

//
 // Act
 //
 target.Execute(serviceProvider);

//
 // Assert
 //
 Assert.AreEqual(expected, actual);
 }