CRM 4.0 Plug-in Developer Tool Extended

Published 2/14/2008 by Henry in CRM

I am in the process of developing CRM 4.0 Plugins. This is very different from the formerly known Callouts.
One of the differences is that these assemblies can be deployed to the database, they need to be registered inside CRM.  When plug-ins are registered in Microsoft Dynamics CRM, they become part of the primary operation of the CRM system. Which is different from CRM 3.0 also.
The object model is different, now the assembly must implement the IPlugin interface that lives inside the Microsoft.Crm.Sdk assebmly that comes witk the CRM 4.0 SDK.

Registering the Plugin is done by using API's provided by the CRM 4.0 SDK. The SDK provides the (C#) sourcecode that compiles into a Windows Forms Application that calls these API's to register a Plugin.
With this tool you can register the plug-in on disk or in the database. Here a screenshot from the mainform of that application the 'Plugin Registration Tool for CRM 4.0': 

CRM 4.0 Plugin Developer Tool
Plugin Registration Tool for CRM 4.0

While struggling with this process, I stumbled on the next tool:

This tool is more complete than the 'Plugin Registration Tool for CRM 4.0' provided as sample code with the SDK. It is developed by the Microsoft Dynamic CRM Team.

New and re-engineered PluginRegistration tool
New and re-engineered PluginRegistration tool

The team addressed 3 core scenarios with this tool.

  1. Developer Scenario:
    An ISV creates a plug-in and registers it on Contact Create as PreCallout (Aka BeforeMainOperation Plug-in). He can then debug the plug-in by registering on Disk. Once the errors are fixed, he can then update the pluginassembly to be uploaded to the database.
  2. Deployment Scenario:
    An ISV builds few plug-ins and registers on multiples steps. Images are registered on few of them. He tests them in the Dev environment but would like to port the registrations to a different organization. PluginRegistration tool supports Export and Import of the solution xml to solve this scenario.
  3. Admin Console:
    Couple of ISV installs their plug-ins on a Customer Organization. Admin at the Customer side saw some problems with “Contact Create” action. He would like to know what plug-in are fired and what the pipeline is for the “Contact – Create”. He then finds the trouble causing plug-in and disables it. So it is all about troubleshooting. PluginRegistration tool solves this problem by allowing Enable/Disable/Unregister operations on steps, plug-in.

The "New and re-engineered PluginRegistration tool" as the team calls it themselves on their blog can be found on here:

Henry Cordes
My thoughts exactly....

Unit test controller in MVC Framework

Published 2/9/2008 by Henry in ASP.NET | C#
Tags: ,

I started using the MVC Framework. I wanted to see if it is for me, because I am a big fan of TDD, but with web apps for me at least, it always is a struggle.
After having a bit of a startup problem, I really started to see the point after a while. The decoupling of concerns, with the Controller as entrypoint, that gets the data from a model and hands it over to the View is a really elegant solution.

Model View Presenter
With the MVP pattern (Model View Presenter) the aspx Page was the entrypoint, it delegated the call to the Presenter only to be called again. This is no decoupling.
With MVC the controller is the spider in the web. The greatest advantages I see in this Framework (or designpattern) is:

  • Allows extensive unit testing (use any unit testing framework you want to (MS Test, NUnit, MBUnit, etc).
  • Separation of concerns, all core contracts within the framework are interface based and therefore mockable
  • No more UrlRewriting, you can model your Url's completely(UrlRoutingEngine)
  • Extensible and pluggable (it is possible to use IOC containers like Windsor, Spring.Net, NHibernate, etc)

A picture to show how the project templates look in the Project Explorer, this is how we create an MVCApplication.

New MVC Application Project

By choosing the 'ASP.NET MVC Web Application and Test' projecttemplate, we get a Test project in our solution also. The MvcApplication's structure is structured around folders.
We get a Content folder, where the css file is located. And we get the: Models, Views and Controllers folders.

New Project structure

Also we get a Test Project in the solution. So how do we test a controller, how do we go about in isolating this 'unit' (the controller), so we can really 'unit test' it?

Interface for DataSource
I want to really decouple the controller so I can test only the functionality provided by the controller.

It needs to be possible to switch the real model for a 'test' or 'mock' version, this apllies also to the view. It also must be possible for the view to be 'mocked' like that.  

What I want is a to show a page containing AuditHistory. So I started to create an Interface that defines the DataSource (IAuditHistoryDataSource).

   1:  using System;
   2:  using System.Collections.Generic;
   4:  namespace MvcApplication.AuditInfo
   5:  {
   6:      public interface IAuditHistoryDataSource
   7:      {
   8:          List<AuditHistory> GetAuditHistoryData();
   9:          List<AuditHistory> GetAuditHistoryDataByUserName(string userName);
  10:      }
  11:  }
Code listing 1: 'IAuditHistoryDataSource'

For the applications data I used LINQ to SQL, that allows me to select the database and with the click of a button generating the code to get the data from my database. Of course you can use whatever datasource you want, LINQ to Entities, LINQ to AD, plain old Datasets, ORM created datasources, NHibernate etc.

The name of the LINQ to SQL class is AuditDataContext:

LINQ to SQL Datamodel

The Model gets a private read-only IAuditHistoryDataSource property that is instantiated with a new instance of AuditDataContext when the setter is called while the private variable _DataSource is null.
Using the 'Inversion of Control' or 'Dependency Injection' pattern (which actually is nothing more than paramaterizing the constructor with the object you want the caller to be responsible for). The Model contains only code that calls methods on this IAuditHistoryDataSource Datasource.

   1:  using System;
   2:  using System.Collections.Generic;
   4:  using MvcApplication.Data.AuditHistory;
   6:  namespace MvcApplication.AuditInfo.Models
   7:  {
   8:      public class AuditDataModel
   9:      {
  10:          private IAuditHistoryDataSource _DataSource;
  11:          private IAuditHistoryDataSource DataSource
  12:          {
  13:              get 
  14:              { 
  15:                  if (_DataSource == null)
  16:                  {
  17:                      _DataSource = new AuditDataContext();
  18:                  }
  19:                  return _DataSource;
  20:              }
  21:          }
  23:          /// <summary>
  24:          /// Constructs an instance of AuditDataModel inversing control of the datasource to the caller
  25:          /// </summary>
  26:          /// <param name="dataSource"></param>
  27:          public AuditDataModel(IAuditHistoryDataSource dataSource)
  28:          {
  29:              _DataSource = dataSource;
  30:          }
  32:          /// <summary>
  33:          ///  Gets all AuditItems (by calling a method on the IAuditHistoryDataSource)
  34:          /// </summary>
  35:          /// <returns></returns>
  36:          public List<AuditHistory> GetAuditHistoryData()
  37:          {
  38:              return DataSource.GetAuditHistoryData();
  39:          }
  41:          /// <summary>
  42:          ///  Gets all AuditItems belonging to a particular User with UserName equal 
  43:          ///  to parameter (by calling a method on the IAuditHistoryDataSource)
  44:          /// </summary>
  45:          /// <param name="userName"></param>
  46:          /// <returns></returns>
  47:          public List<AuditHistory> GetAuditHistoryDataByUserName(string userName)
  48:          {
  49:              return DataSource.GetAuditHistoryDataByUserName(userName);
  50:          }
  51:      }
  52:  }
Code listing 2

Using this technique it is now easy to mock the datasource for test purposes. Doing TDD we first write our test, than we write code. To keep this post understandable I choose to write it the other way around. I do not want to bore you with all my mistakes and refactorings, but provide the working solution.
Next I will list the Controller, which like the Model, uses the 'Inversion of Control' or 'Dependency Injection' pattern and takes a IAuditHistoryDataSource as a parameter in its constructor. The Controller than passes this dataSource to the constructor of the Model it uses.

   1:  using System;
   2:  using System.Web;
   3:  using System.Web.Mvc;
   4:  using System.Collections.Generic;
   6:  using MvcApplication.Data.AuditHistory;
   7:  using MvcApplication.AuditInfo.Models;
   9:  namespace MvcApplication.AuditInfo.Controllers
  10:  {
  12:      public class AuditingController : Controller
  13:      {
  14:          private AuditDataModel AuditData;
  16:          public AuditingController()
  17:          {
  18:              AuditData = new AuditDataModel();
  19:          }
  21:          public AuditingController(IAuditHistoryDataSource dataSource)
  22:          {
  23:              AuditData = new AuditDataModel(dataSource);
  24:          }
  26:          [ControllerAction]
  27:          public void Index()
  28:          {
  29:              List<AuditHistory> auditHistory = AuditData.GetAuditHistoryData();
  30:              ViewData.Add("AuditHistoryAll", auditHistory);
  31:              RenderView("List", auditHistory);
  32:          }
  34:          [ControllerAction]
  35:          public void List(string id)
  36:          {
  37:              List<AuditHistory> auditHistory = AuditData.GetAuditHistoryDataByUserName(id);
  38:              ViewData.Add("AuditHistoryByUserName", auditHistory);
  39:              RenderView("List", auditHistory);
  40:          }
  41:      }
  42:  }
Code listing 3

As you can see the MVC Framework uses attributes to define Actions (ControllerAction). The Framework uses these so the right Action (method) is mapped to a url.
Scott Guthrie wrote a couple of blogposts on the subject. He wrote a series of 4 posts, this is part 1:

I want to write tests against the Controller, To do this, first I have to find a way to override the part where the view gets rendered. We do not want to render the view, we want to test if the controller gets the data and passes it to the (right) view.  In the Test project we create a AuditingControllerMock which inherits from the Controller we want to test. In the AuditingControllerMock we override the RenderView method and the only thing we do in it is get the viewName passed in through a parameter and assign it to a property: 'RenderViewAction'.

   1:  using MvcApplication.AuditInfo.Controllers;
   2:  using MvcApplication.Data.AuditHistory;
   4:  namespace MvcApplication.AuditInfo.Test
   5:  {
   6:      public class AuditingControllerMock: AuditingController
   7:      {
   8:          public string RenderViewAction { get; set; }
  10:          public AuditingControllerMock(IAuditHistoryDataSource dataSource):base(dataSource)
  11:          {
  12:          }
  14:          protected override void RenderView(string viewName, string masterName, object viewData)
  15:          {
  16:              RenderViewAction = viewName;
  17:          }
  18:      }
  19:  }
Code listing 4

In the actual Test project I decided to use Rhino Mocks, because it helps me mock the DataSource in just a few lines of code.

   1:  using System.Collections.Generic;
   2:  using Microsoft.VisualStudio.TestTools.UnitTesting;
   4:  using Rhino.Mocks;
   6:  using MvcApplication.Data.AuditHistory;
   7:  using System;
   9:  namespace MvcApplication.AuditInfo.Test.Controllers
  10:  {
  12:      /// <summary>
  13:      ///  This is a test class for AuditingControllerTest and is intended
  14:      ///  to contain all AuditingControllerTest Unit Tests
  15:      ///</summary>
  16:      [TestClass()]
  17:      public class AuditingControllerTest
  18:      {
  19:          /// <summary>
  20:          ///A test for Index, needs to return all items in list
  21:          ///</summary>
  22:          [TestMethod()]
  23:          public void AuditingController_Get_All_AuditHistory_items_IndexTest()
  24:          {
  25:              MockRepository mocks = new MockRepository();
  27:              IAuditHistoryDataSource mockDataSource = mocks.CreateMock<IAuditHistoryDataSource>();
  28:              List<AuditHistory> auditHistoryList = GetBaseTestList();
  30:              AuditingControllerMock controller = new AuditingControllerMock(mockDataSource);
  31:              Expect.Call(mockDataSource.GetAuditHistoryData()).Return(auditHistoryList);
  33:              mocks.ReplayAll();
  34:              controller.Index();
  35:              mocks.VerifyAll();
  37:              Assert.IsNotNull(controller.ViewData["AuditHistoryAll"], "Controller AuditHistoryAll returned null");
  38:              Assert.IsTrue(((List<AuditHistory>)controller.ViewData["AuditHistoryAll"]).Count > 0, 
  39:                              "AuditHistoryAll did not return expected number of items AuditHistory Count is not greater than 0");
  41:          }
  43:          /// <summary>
  44:          ///A test for List(userName), returns all AuditHistory belonging to a particular user
  45:          ///</summary>
  46:          [TestMethod()]
  47:          public void AuditingController_Get_AuditHistory_By_UserName_ListTest()
  48:          {
  49:              MockRepository mocks = new MockRepository();
  51:              IAuditHistoryDataSource mockDataSource = mocks.CreateMock<IAuditHistoryDataSource>();
  52:              List<AuditHistory> auditHistoryList = GetBaseTestList();
  54:              AuditingControllerMock controller = new AuditingControllerMock(mockDataSource);
  55:              Expect.Call(mockDataSource.GetAuditHistoryDataByUserName(UserNameToTest)).Return(auditHistoryList);
  57:              mocks.ReplayAll();
  58:              controller.List(UserNameToTest);
  59:              mocks.VerifyAll();
  61:              Assert.IsNotNull(controller.ViewData["AuditHistoryByUserName"], "Controller AuditHistoryByUserName returned null");
  62:              Assert.IsTrue(((List<AuditHistory>)controller.ViewData["AuditHistoryByUserName"]).Count > 0, 
  63:                              "AuditHistoryByUserName did not return expected number of items AuditHistory Count is not greater than 0");
  64:          }
  67:          private Guid UserIDToTest = new Guid("aa6d5659-fd41-44f1-b838-8c88b0b753e6");
  68:          private Guid ObjectIDToTest = new Guid("1b884382-d51a-4b42-a1f0-d1d1b1adc864");
  69:          private int ObjectTypeToTest = 1;
  70:          private string UserNameToTest = "Bill";
  72:          private List<AuditHistory> GetBaseTestList()
  73:          {
  74:              List<AuditHistory> auditHistoryList = new List<AuditHistory>
  75:              {
  76:                  GetAuditHistory(UserNameToTest,
  77:                                   UserIDToTest,
  78:                                   "Test operation",
  79:                                   ObjectTypeToTest,
  80:                                   ObjectIDToTest,
  81:                                   DateTime.Now,
  82:                                   "Audit data to test"),
  83:                  GetAuditHistory(UserNameToTest,
  84:                                   UserIDToTest,
  85:                                   "Test operation 2",
  86:                                   ObjectTypeToTest,
  87:                                   ObjectIDToTest,
  88:                                   DateTime.Now,
  89:                                   "Audit data 2 to test"),
  90:                  GetAuditHistory(UserNameToTest,
  91:                                   UserIDToTest,
  92:                                   "Test operation 3",
  93:                                   ObjectTypeToTest,
  94:                                   ObjectIDToTest,
  95:                                   DateTime.Now,
  96:                                   "Audit data 3 to test")
  97:              };
  98:              return auditHistoryList;
  99:          }
 101:          private AuditHistory GetAuditHistory(string userName,
 102:                                               Guid userId,
 103:                                               string operation,
 104:                                               int objectType,
 105:                                               Guid objectId,
 106:                                               DateTime modifiedOn,
 107:                                               string auditData)
 108:          {
 109:              AuditHistory audit = new AuditHistory();
 110:              audit.AuditData = auditData;
 111:              audit.AuditID = Guid.NewGuid();
 112:              audit.ModifiedOn = modifiedOn;
 113:              audit.ObjectId = objectId;
 114:              audit.ObjectType = objectType;
 115:              audit.Operation = operation;
 116:              audit.UserId = userId;
 117:              audit.UserName = userName;
 119:              return audit;
 120:          }
 121:      }
 122:  }
Code listing 5

In both tests I create a Mock of the Datasource by passing the IAuditHistoryDataSource to the CreateMock method of Rhino Mocks. I assign a filled List of Type AuditHistory to the variable auditHistoryList with the following line:

   52:  List auditHistoryList = GetBaseTestList(); 
Code listing 6

Next I tell Rhino Mock what method it needs to expect to be called and what that method must return, with the line:

   55:  Expect.Call(mockDataSource.GetAuditHistoryDataByUserName(UserNameToTest)).Return(auditHistoryList); 
Code listing 7

With the next three lines we tell Rhino Mocks it needs to pay attention(line 1. In line 2 we call the ControllerAction 'List' on our Controller. In line 3 Rhino Mocks Verifies if all our assertions are being met:

   57:  mocks.ReplayAll();
   58:  controller.List(UserNameToTest);
   59:  mocks.VerifyAll();
Code listing 8

After that I check if the returned data in the View is not null and if the number of items in the List of type AuditHistory is greater than 0.

My conclusion is that the testability indeed is lots better with the MVC Framework. There is a learning curve, it seemed a bit strange to use the 'classic ASP' way of 'spagetthi coding' in the html, but the separation of concerns is really a big advantage, that not only provides better testing but also better maintainable code.
If the server controls are obsolete now is too much to say, but teams that will use this Framework are going to have to learn another way of working and cannot use the server controls we got used to.
I really want to try out the MVC Toolkit, which provides UIHelpers and otherstuff that is usefull when using the MVC Framework, I did not get the change to work with it, but when I do I hope to post about it here.

MVC Framework links: 
ASP.NET Forum on MVC Framework 

Henry Cordes
My thoughts exactly...