Random Musings

July 24, 2009

Is it possible to register different event handlers in only one assembly to be registered in Dynamics CRM Plugin ?

Filed under: Dynamics CRM — haditeo @ 9:41 am
Tags:

The answer is Yes.

plugin_registration_tool_main_window

The reason why i need this, is because i would like to register only a single DLL for the Plugin DLL, let’s called it XXX.CRM.Plugin

visual_studio_solution

To register multiple event handler, create multiple classes that implements Execute method of the IPlugin interface, then automatically when you register the plugin, you can load those classes

register_assembly

Now, the question is how do i select which class to be loaded against my event message ? When you register a new step, there is a plugin dropdownlist, you can select which class to be executed. Since we are registering two classes, then there will be two plugin options in the drop down list

select_which_plugin_to_be_executed

Advertisements

June 11, 2009

TDD with Dynamics CRM 4.0 Plugin

Filed under: Dynamics CRM — haditeo @ 4:24 pm
Tags: , ,

Download source code here

I would like to introduce how to enforce TDD for developing plugin in Dynamic CRM. Why enforcing TDD ? I find it very time consuming to manually enter the data from the Dynamics CRM UI, just to trigger the plugin to run and generate the IPluginExecutionContext.

Usually i hook the debugger on the plugin codes right inside the IPluginExecutionContext method and debug line by line, then if some bugs are detected, then i need to
1) Recompile
2) Re-register again through plugin registration tool
3) IISReset
4) Manually enter the data again
5) Debug the codes
These cycles are repeated numerously, which are time consuming

The key is how do we “mock” IPluginExecutionContext ?

Take a look at the definition of the Execute method which accepts IPluginExecutionContext type of object :

public void Execute(IPluginExecutionContext context)
{
    ContextStage = context.Stage;

    List = QueryActiveContact(context.CreateCrmService(true));
}

In this method, i would like to
1) Mock the context.Stage property to return some fake value
2) Run a query against Dynamics CRM SDK by mocking the CrmService returned by context.CreateCRMService() method

Here i am using RhinoMock as my mocking framework:

[Test]
public void TestInstantiate()
{
    TestIPluginExecutionContext test = new TestIPluginExecutionContext();           

    MockRepository mocks = new MockRepository();
    IPluginExecutionContext context = mocks.StrictMock<IPluginExecutionContext>();

    With.Mocks(mocks).Expecting(delegate
    {
        Expect.Call(context.Stage).Return(11122);
        Expect.Call(context.CreateCrmService(true)).Return(new MockCrmService());
    })
    .Verify(delegate
    {
        test.Execute(context);
    });

    Assert.Greater(test.ContextStage, 0, "context stage is 0");
    Assert.IsNotEmpty(test.List, "list is empty");
}

The key here is i am using Expect.Call() method, to tell mocking framework to return ‘11122’ integer value when context.Stage is invoked . If the context.CreateCrmService() method is invoked, please return a concrete MockCrmService object. I have created a fake MockCrmService object, to be injected when the context.CreateCrmService() method is called.

I have noticed that the Dynamics CRM plugin has confirmed to TDD, by making the parameter of the method of Execute accepting an Interface type of IPluginExecutionContext. This is very useful, since during the implementation we can mock / fake the CRM Service by impersonating any CRM user. The advantage of this is to test whether any exception is occured when we impersonate a CRM user to access a record.

Here is the concrete MockCrmService object

public class MockCrmService : ICrmService
{
    public CrmService CreateCrmService()
    {
        NetworkCredential nc = new NetworkCredential();
        nc.Domain = Settings.Default.Domain;
        nc.UserName = Settings.Default.UserName;
        nc.Password = Settings.Default.Password;

        CrmAuthenticationToken token = new CrmAuthenticationToken();
        token.AuthenticationType = Convert.ToInt32(Settings.Default.AuthenticationType); ;
        token.OrganizationName = Settings.Default.OrgName;

        CrmService crmService = new CrmService();
        crmService.CrmAuthenticationTokenValue = token;
        crmService.Credentials = nc;
        crmService.Url = Settings.Default.CrmUrl;

        return crmService;
    }

    #region ICrmService Members

    public Guid Create(BusinessEntity entity)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public void Delete(string entityName, Guid id)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public object Execute(object request)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public string Fetch(string fetchXml)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public BusinessEntity Retrieve(string entityName, Guid id, Microsoft.Crm.Sdk.Query.ColumnSetBase columnSet)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public BusinessEntityCollection RetrieveMultiple(Microsoft.Crm.Sdk.Query.QueryBase query)
    {
        return CreateCrmService().RetrieveMultiple(query);
    }

    public void Update(BusinessEntity entity)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion
}

Since we are interested in using RetrieveMultiple method, i inserted codes below to instantiate a new CrmService object

    public BusinessEntityCollection RetrieveMultiple(Microsoft.Crm.Sdk.Query.QueryBase query)
    {
        return CreateCrmService().RetrieveMultiple(query);
    }

For the full source code, please refer to the download link in the beginning of the post.
What are the advantages of using TDD in developing plugin ?
1) We can “mock” the IPluginExecutionContext to supply correct data to test our internal business logic.
2) We can “mock” the IPluginExecutionContext to supply incorrect data to test whether the exception raised by our business logic are correct
3) We can combine this with NUnit to run our test cases automatically, without needing manual intervention. Since we build small codes and test small codes, it’s quite easy to detect bugs since we have NUnit to alert us in the event of bugs raising.
4) We don’t need to publish the plugin just to test the plugin, since publishing is time consuming and involving IISReset to refresh the assembly.
5) We can utilize NUnit to put assertion, to detect whether the object values or states are correct, rather than manually put the debugger when the plugin is running.

Create a free website or blog at WordPress.com.