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

Introduction

This is the first post of a series containing collections of patterns and code fragments showing how various unit frameworks can be used in Dynamics CRM 2011 development.

Unit test with server side components, (web service, plugin, workflow), will be explained with Microsoft Fakes, Rhino Mocks and Moq.

Unit test with client side JavaScript will be explained with QUnit.

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

Series Contents

Part 1: Introduction and Series Contents

  • Introduces the topics that will be covered in the series

Part 2: Microsoft Fakes with LINQ query

  • Unit test CRM LINQ query
  • Mock System.Linq namespace
  • Mock Mock OrganizationServiceContext.CreateQuery
  • Mock OrganizationServiceContext
  • Create SaveChangesResultCollection
  • Create SaveChangesResult

Part 4: Microsoft Fakes with CrmOrganizationServiceContext

  • Mock CrmOrganizationServiceContext

Part 5: Microsoft Fakes with LINQ query through IOrganizationService

  • Mock IOrganizationService to isolate LINQ to CRM query

Part 6: Microsoft Fakes with OrganizationServiceContext through IOrganizationService

  • Isolate method call to OrganizationServiceContext through mocking IOrganizationService

Part 7: Microsoft Fakes with CrmOrganizationServiceContext through IOrganizationService

  • Isolate method call to OrganizationServiceContext extension methods through mocking IOrganizationService
  • Isolate method call to CrmOrganizationServiceContext through mocking IOrganizationService

Part 8: Microsoft Fakes with IOrganizationService

  • Using Stub type to isolate method call to IOrganizationService

Part 9: Microsoft Fakes with CRM plugin

  •  Mock IOrganizationService, IPluginExecutionContext, ITracingService, IOrganizationServiceFactory and IServiceProvider

Part 10: Microsoft Fakes with workflow activity

  • Correctly invoke CodeActivity.Execute(CodeActivityContext executionContext)
  • Correctly pass in InArgument<T> properties
  • Pass in required service as extensions

Part 11: QUnit with client side JavaScript

  • QUnit
  • Xrm.Page Script Library Template (Microsoft Visual Studio extension)

Part 12: Rhino Mocks with LINQ query, OrganizationServiceContext and CrmOrganizationServiceContext

Part 13: Rhino Mocks with IOrganizationService

Part 14: Rhino Mocks with CRM plugin

Part 15: Rhino Mocks with workflow activity

Part 16: Moq with LINQ query, OrganizationServiceContext and CrmOrganizationServiceContext

Part 17: Moq with IOrganizationService

Part 18: Moq with CRM plugin

Part 19: Moq with workflow activity

Dynamics CRM 2011 Portal Development

Microsoft Dynamics CRM 2011 SDK contains portal developer guide, assemblies and portalbase solution to help creating portal Web pages that interact with Microsoft Dynamics CRM.

Microsoft Dynamics Labs has created “Customer Portal for Microsoft Dynamics CRM 2011” and “Partner Relationship Management Portal for Microsoft Dynamics CRM 2011” which can be accessed in Microsoft Dynamics market place.

This article series introduce how to use assemblies (microsoft.xrm.client.dll & microsoft.xrm.portal.dll) and portalbase solution to build portal.

  1. Getting Started
  2. ASP.NET Expression
  3. Data Source Web Server Controls
  4. ASP.NET Data-Bound Web Server Controls
    • CrmEntityEditingManager
    • CrmEntityFormView
    • CrmListView
    • CrmSiteMapChildView
    • Property
    • Snippet
    • WebLinks
  5. ASP.NET Web Server Controls
    • CrmHyperLink
    • DateTimeLiteral
    • EntityRightsView
    • LiveIdLoginStatus
    • WebLinkHyperLink
    • SiteEditingManager

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);
 }

Dynamics CRM 2011 Unit Test Part 11: QUnit with client side JavaScript

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

The complete sample code using QUnit can be downloaded from MSDN sample gallery: http://code.msdn.microsoft.com/Dynamics-CRM-2011-client-9f189663

This post describe how to use QUnit and Xrm.Page Script Library Template (Microsoft Visual Studio extension) to unit test Dynamics CRM 2011 client side jQuery or JavaScript code.

More information on Xrm.Page Script Library Templatecan be found: http://msdn.microsoft.com/en-us/library/gg328261.aspx#BKMK_DevelopmentTools

More information on QUnit can be found: http://qunitjs.com/

Suppose we have one JavaScript web resource which will run when account form load. The client side JavaScript code will update the “Account Name” label to “Organisation Name” and set Preferred User look up field value to the Preferred User of the Primary Contact of the current account.

Code Under Test


/// <reference path="XrmPageTemplate.js" />

//This Library uses a namespace naming strategy to help prevent duplication function names
if (typeof (Zzhou) == "undefined")
{ Zzhou = { __namespace: true }; }
if (typeof (Zzhou.Qunit) == "undefined")
{ Zzhou.Qunit = { __namespace: true }; }
// Namespace container for functions in this library.
Zzhou.Qunit.Account = {
 onload: function () {
 var attrName = 'name',
 label = 'Organisation Name';
 this._changeControlLabel(attrName, label);
 this._updatePreferredUser();
 },
 _changeControlLabel: function (attrName, label) {
 var control = Xrm.Page.getControl(attrName);
 if (control) {
 control.setLabel(label);
 }
 },
 _updatePreferredUser: function () {
 // primary contact look up field name
 var attrName = 'primarycontactid';
 // get primary contact attribute
 var attribute = Xrm.Page.getAttribute(attrName);
 if (attribute) {
 // get primary contact attribute value
 var attrValue = attribute.getValue();
 if (attrValue && attrValue[0] && attrValue[0].entityType === 'contact') {
 // get primary contact id
 var primaryContactId = attrValue[0].id;
 // retrieve primary contact.PreferredSystemUserId
 SDK.JQuery.retrieveRecord(
 primaryContactId,
 "Contact",
 "PreferredSystemUserId",
 null,
 // success call back
 function (user) {
 if (user && user.PreferredSystemUserId) {
 var attrName = 'preferredsystemuserid';
 var attribute = Xrm.Page.getAttribute(attrName);
 if (attribute) {
 var lookupReference = [];
 lookupReference[0] = {};
 lookupReference[0].id = user.PreferredSystemUserId.Id;
 lookupReference[0].entityType = user.PreferredSystemUserId.LogicalName;
 lookupReference[0].name = user.PreferredSystemUserId.Name;

attribute.setValue(lookupReference);
 }
 }
 },
 function (error) {
 alert(error.message);
 }
 );
 }
 }
 },
 __namespace: true
};

Unit Test


/// <reference path="XrmPageTemplate.js" />
module(
 'Account Form Library Test',
 {
 setup: function () {
 this.savedAjax = $.ajax;
 },
 teardown: function () {
 $.ajax = this.savedAjax;
 }
 }
 );
test("Zzhou.Qunit.Account._changeControlLabel", function () {
 //
 // Arrange
 //
 var attrName = 'name',
 expectedLabel = 'Organisation Name';
 //
 // Act
 //
 Zzhou.Qunit.Account._changeControlLabel(attrName, expectedLabel);

//
 // Assert
 //
 var actualLabel, control = Xrm.Page.getControl(attrName);
 if (control) {
 actualLabel = control.getLabel();
 }
 strictEqual(actualLabel, expectedLabel, "actualLabel and expectedLabel are the same value and type");
});
test("Zzhou.Qunit.Account._updatePreferredUser", function () {
 expect(3);

//
 // Arrange
 //
 var data = {
 d: {
 PreferredSystemUserId: {
 Id: '5bcf7e47-9993-e111-88fa-00155d4c5b01',
 LogicalName: 'systemuser',
 Name: 'First name Last name'
 }
 }
 };
 $.ajax = function (options) {
 options.success(data, "success", { responseText: JSON.stringify(data) });
 };

//
 // Act
 //
 Zzhou.Qunit.Account._updatePreferredUser();

//
 // Assert
 //
 var attrName = 'preferredsystemuserid';
 var attribute = Xrm.Page.getAttribute(attrName);
 if (attribute) {
 var attrValue = attribute.getValue();
 if (attrValue && attrValue[0]) {
 strictEqual(attrValue[0].entityType, data.d.PreferredSystemUserId.LogicalName);
 strictEqual(attrValue[0].id, data.d.PreferredSystemUserId.Id);
 strictEqual(attrValue[0].name, data.d.PreferredSystemUserId.Name);
 }
 }
});

Dynamics CRM 2011 Rollup Update 11 IFD not working Failed to get priv user group information

Update:

 

A fix can be found from the following link

http://community.dynamics.com/product/crm/f/117/t/93178.aspx

Microsoft just provided me with a work around while they work on the hot fix.

• Change the Anonymous user identity of IIS Anonymous Authentication Credentials to Application pool Identity option:

1. On the CRM server, open the Internet Information Services (IIS) Manager;

2. In IIS Manager, click the CRM site;

3. In the Features View, double-click Authentication;

4. Select Anonymous Authentication , and then click Edit in the Actions pane;

5. In the Edit Anonymous Authentication Credentials dialog box, click the Application pool Identity , and then click Ok;

6. Do an IISRESET on CRM and ADFS server.

 

 

I have on premise IFD deployment, after install Rollup Update 11, it is not working, i keep get the following errors


Microsoft CRM Error Report:
Error Description:
Failed to get priv user group information. k = 1b813725-c917-4291-bcea-277fca8e799e privUserGroupId: 5140579c-a508-40a3-943b-fed25dac4842, localSystemAdGuid: 00000000-0000-0000-0000-000000000000, Exception: Microsoft.Crm.CrmSecurityException: Could not find GUID for server: VMCRM$ With SearchFilter:samAccountName at Microsoft.Crm.SecurityUtils.GetGuid(String searchItem, String searchFilter, String searchItemLogInfo, Boolean exceptionIfNotfound) at Microsoft.Crm.SecurityUtils.GetLocalSystemGuid() at Microsoft.Crm.Caching.OrganizationSettingsCacheLoader.LoadCacheData(Guid key, ExecutionContext context).

Error Details:
Failed to get priv user group information. k = 1b813725-c917-4291-bcea-277fca8e799e privUserGroupId: 5140579c-a508-40a3-943b-fed25dac4842, localSystemAdGuid: 00000000-0000-0000-0000-000000000000, Exception: Microsoft.Crm.CrmSecurityException: Could not find GUID for server: VMCRM$ With SearchFilter:samAccountName
 at Microsoft.Crm.SecurityUtils.GetGuid(String searchItem, String searchFilter, String searchItemLogInfo, Boolean exceptionIfNotfound)
 at Microsoft.Crm.SecurityUtils.GetLocalSystemGuid()
 at Microsoft.Crm.Caching.OrganizationSettingsCacheLoader.LoadCacheData(Guid key, ExecutionContext context).

Full Stack:
[CrmSecurityException: Could not find GUID for server: VMCRM$ With SearchFilter:samAccountName]
 at Microsoft.Crm.SecurityUtils.GetGuid(String searchItem, String searchFilter, String searchItemLogInfo, Boolean exceptionIfNotfound)
 at Microsoft.Crm.SecurityUtils.GetLocalSystemGuid()
 at Microsoft.Crm.Caching.OrganizationSettingsCacheLoader.LoadCacheData(Guid key, ExecutionContext context)

[CrmException: Failed to get priv user group information. k = 1b813725-c917-4291-bcea-277fca8e799e privUserGroupId: 5140579c-a508-40a3-943b-fed25dac4842, localSystemAdGuid: 00000000-0000-0000-0000-000000000000, Exception: Microsoft.Crm.CrmSecurityException: Could not find GUID for server: VMCRM$ With SearchFilter:samAccountName
 at Microsoft.Crm.SecurityUtils.GetGuid(String searchItem, String searchFilter, String searchItemLogInfo, Boolean exceptionIfNotfound)
 at Microsoft.Crm.SecurityUtils.GetLocalSystemGuid()
 at Microsoft.Crm.Caching.OrganizationSettingsCacheLoader.LoadCacheData(Guid key, ExecutionContext context).]
 at Microsoft.Crm.Caching.OrganizationSettingsCacheLoader.LoadCacheData(Guid key, ExecutionContext context)
 at Microsoft.Crm.Caching.ObjectModelCacheLoader`2.LoadCacheData(TKey key, IOrganizationContext context)
 at Microsoft.Crm.Caching.CrmMultiOrgCache`2.CreateEntry(TKey key, IOrganizationContext context)
 at Microsoft.Crm.Caching.CrmMultiOrgCache`2.LookupEntry(TKey key, IOrganizationContext context)
 at Microsoft.Crm.Authentication.Claims.ClaimsUtility.GetAuthenticationMethod(Guid organizationId, String method)
 at Microsoft.Crm.Authentication.Claims.CrmFederatedAuthenticationModule.SendToIdentityProvider(RedirectLocation redirectLocation)
 at Microsoft.Crm.Authentication.Claims.CrmFederatedAuthenticationModule.OnEndRequestHandler(Object sender, EventArgs e)
 at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
 at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Other Message:

Error Number:
0x80040216

Source File:
Not available

Line Number:
Not available

Error Trace:

&nbsp;

Date: 10-30-2012

Time: 20:09:56

Server: default.zhongchenzhou.com
Request URL:

https://default.zhongchenzhou.com/default.aspx

Dynamics CRM 2011 IE10 cannot import solution script error

When use IE10 together with Dynamics CRM 2011 (Rollup Update 10) and try to import a solution, there is a error window popup

This is caused by a script error in file

~\Tools\Solution\import\SolutionImportProcess.aspx
function window.onload() {

After change the code to

window.onload = function () {

Another error comes

 

If click on cancel, another error comes

 

Anyone know how to fix this?

How to use Kendo UI DataSource, Kendo UI Grid with Dynamics CRM 2011 REST Endpoint Sample 1

The complete source code can be download from sample gallery: Kendo UI DataSource, Kendo UI Grid with Dynamics CRM 2011 REST Endpoint

Kendo UI is a HTML5, jQuery-based framework for building modern HTML apps. Kendo UI combines the best of emerging HTML5, CSS3, and JavaScript technologies with robust, cross-browser techniques to deliver a framework that is both powerfully rich and broadly compatible with older browsers.

Microsoft Dynamics CRM 2011 Release 8 include support for IE, Chrome, Firefox and Safari.

By using a powerful JavaScript framework, the use of ASP.NET is reduced which would remove the complexity of deployment which also makes the solution much more portable.

By using Kendo UI DataSource, Kendo UI Grid with Dynamics CRM 2011 REST Endpoint, it is easy to implement server side filter, sorting and paging.

There are a few tricks to use Kendo UI together with Dynamics CRM 2011 I want to share. The complete code is in the end of this post.

1. Microsoft Dynamics CRM 2011 REST Endpoint does not support $format system query option.

You can use a global jQuery ajax event handler to specifying http header ensures that the results will be returned as JSON.


$(document).ajaxSend(function (e, jqxhr, settings) {
 if (settings.url.toLowerCase().indexOf("XRMServices/2011/OrganizationData.svc".toLowerCase()) >= 0) {
 jqxhr.setRequestHeader("Accept", "application/json");
 }
});

2. Microsoft Dynamics CRM 2011 REST Endpoint does not support $inlinecount, $count system query option.

you can set a function to use fetch xml and soap endpoint to return the total record count.


var dataSource = new kendo.data.DataSource({
 schema: {
 total: function (data) {
var fetchXml = '<fetch mapping="logical" aggregate="true">' +
 '<entity name="account">' +
 "<attribute name='accountid' alias='count' aggregate='count'/>" +
 '</entity>' +
 '</fetch>';

var context = GetGlobalContext();

var serverUrl = window.parent.document.location.protocol + '//' + window.parent.document.location.host + '/' + context.getOrgUniqueName();

var _oService = new FetchUtil(context.getOrgUniqueName(), serverUrl);
 var res = _oService.Fetch(fetchXml);

var count = res[0].attributes.count.formattedValue;

return count;
 }
 }
});

3. you need to stop Kendo UI to send default query string parameter and use Dynamics CRM 2011 specific format


var dataSource = new kendo.data.DataSource({
 transport: {
 parameterMap: function (options) {
 return {
 $top: options.take,
 $skip: options.skip,
 $select: 'Name,Telephone1,Address1_City'',
 $orderby: options.sort[0].field + ' ' + options.sort[0].dir
 };
 }
 }
});

4. you have to parse returned json object to return object array that Kendo UI understand


var dataSource = new kendo.data.DataSource({
 schema: {
 parse: function (data) {
 return data.d.results;
 },
 type: "json"
 }
});

Complete Kendo UI DataSource code

var context = GetGlobalContext();

var serverUrl = window.parent.document.location.protocol + '//' + window.parent.document.location.host + '/' + context.getOrgUniqueName();

dataSource = new kendo.data.DataSource({
 transport: {
 read: {
 url: serverUrl + "/XRMServices/2011/OrganizationData.svc/AccountSet",
 dataType: 'json'
 },
 parameterMap: function (options) {
 var parameter = {
 $top: options.take,
 $skip: options.skip,
 $select: 'Name,Telephone1,Address1_City',
 $orderby: options.sort[0].field + ' ' + options.sort[0].dir
 };

return parameter;
 }
 },
 schema: {
 model: {
 id: "AccountId",
 fields: {
 Name: { type: "string" },
 Telephone1: { type: "string" },
 Address1_City: { type: "string" }
 }
 },
 total: function (data) {

var fetchXml = '<fetch mapping="logical" aggregate="true">' +
 '<entity name="account">' +
 "<attribute name='accountid' alias='count' aggregate='count'/>" +
 '</entity>' +
 '</fetch>';

var _oService = new FetchUtil(context.getOrgUniqueName(), serverUrl);
 var res = _oService.Fetch(fetchXml);

var count = res[0].attributes.count.formattedValue;

return count;
 },
 parse: function (data) {
 return data.d.results;
 },
 type: "json"
 },
 serverPaging: true,
 pageSize: 10,
 serverSorting: true,
 sort: { field: "Name", dir: "asc" }
 });

Complete Sample code

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
 <meta charset="utf-8" />
 <title>Kendo UI Demo</title>
 <!-- CDN-based stylesheet references for Kendo UI Web -->
 <link rel="stylesheet" href="http://cdn.kendostatic.com/2012.2.913/styles/kendo.common.min.css" />
 <link rel="stylesheet" href="http://cdn.kendostatic.com/2012.2.913/styles/kendo.default.min.css" />
</head>
<body style="border-width: 0px; padding-left: 0px; padding-top: 0px; margin-left: 0px; margin-top: 0px; margin-bottom: 0px; margin-right: 0px">
 <div id="grid"></div>
 <!-- CDN-based script reference for jQuery -->
 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
 <!-- CDN-based script reference for Kendo UI DataViz -->
 <script type="text/javascript" src="http://cdn.kendostatic.com/2012.2.913/js/kendo.web.min.js"></script>
 <script type="text/javascript" src="ClientGlobalContext.js.aspx"></script>
 <script type="text/javascript">
 $(document).ready(function ($) {

var context = GetGlobalContext();

var serverUrl = window.parent.document.location.protocol + '//' + window.parent.document.location.host + '/' + context.getOrgUniqueName();

dataSource = new kendo.data.DataSource({
 transport: {
 read: {
 url: serverUrl + "/XRMServices/2011/OrganizationData.svc/AccountSet",
 dataType: 'json'
 },
 parameterMap: function (options) {
 var parameter = {
 $top: options.take,
 $skip: options.skip,
 $select: 'Name,Telephone1,Address1_City',
 $orderby: options.sort[0].field + ' ' + options.sort[0].dir
 };

return parameter;
 }
 },
 schema: {
 model: {
 id: "AccountId",
 fields: {
 Name: { type: "string" },
 Telephone1: { type: "string" },
 Address1_City: { type: "string" }
 }
 },
 total: function (data) {

var fetchXml = '<fetch mapping="logical" aggregate="true">' +
 '<entity name="account">' +
 "<attribute name='accountid' alias='count' aggregate='count'/>" +
 '</entity>' +
 '</fetch>';

var _oService = new FetchUtil(context.getOrgUniqueName(), serverUrl);
 var res = _oService.Fetch(fetchXml);

var count = res[0].attributes.count.formattedValue;

return count;
 },
 parse: function (data) {
 return data.d.results;
 },
 type: "json"
 },
 serverPaging: true,
 pageSize: 10,
 serverSorting: true,
 sort: { field: "Name", dir: "asc" }
 });

var grid = $("#grid").kendoGrid({
 dataSource: dataSource,
 height: 450,
 columns: [{
 template: '<input type="checkbox" />',
 sortable: false,
 width: 45
 },
 {
 title: 'Account Name',
 field: "Name",
 width: 180
 },
 {
 title: 'Main Phone',
 field: "Telephone1",
 width: 300
 },
 {
 title: 'Address 1: City',
 field: "Address1_City",
 filterable: false,
 width: 300,
 sortable: false
 }],
 editable: false,
 pageable: true,
 selectable: "multiple, row",
 sortable: {
 mode: 'single',
 allowUnsort: false
 },
 dataBound: function () {
 grid.table.find("tr").find("td:first input")
 .change(function (e) {
 if (!$(this).prop('checked')) {
 grid.clearSelection();
 }
 });
 },
 change: function (e) {
 window.setTimeout(function () {
 var checkbox = $(e.sender.select()[0]).find("td:first input").prop("checked", true);
 grid.table.find("tr").find("td:first input:checked").not(checkbox).prop("checked", false);
 }, 0);
 }
 }).data("kendoGrid");

grid.thead.find("th:first")
 .append($('<input class="selectAll" type="checkbox"/>'))
 .delegate(".selectAll", "click", function () {
 var checkbox = $(this);

grid.table.find("tr")
 .find("td:first input")
 .attr("checked", checkbox.is(":checked"))
 .trigger("change");
 });
 }).ajaxSend(function (e, jqxhr, settings) {
 if (settings.url.toLowerCase().indexOf("XRMServices/2011/OrganizationData.svc".toLowerCase()) >= 0) {
 jqxhr.setRequestHeader("Accept", "application/json");
 }
 });
 </script>
 <script id="FetchUtil" type="text/javascript">
 var XMLHTTPSUCCESS = 200;
 var XMLHTTPREADY = 4;

function FetchUtil(sOrg, sServer) {
 this.org = sOrg;
 this.server = sServer;

if (sOrg == null) {
 if (typeof (ORG_UNIQUE_NAME) != "undefined") {
 this.org = ORG_UNIQUE_NAME;
 }
 }

if (sServer == null) {
 this.server = window.location.protocol + "//" + window.location.host;
 }
 }

FetchUtil.prototype._ExecuteRequest = function (sXml, sMessage, fInternalCallback, fUserCallback) {
 var xmlhttp = new XMLHttpRequest();
 xmlhttp.open("POST", this.server + "/XRMServices/2011/Organization.svc/web", (fUserCallback != null));
 xmlhttp.setRequestHeader("Accept", "application/xml, text/xml, */*");
 xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
 xmlhttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");

if (fUserCallback != null) {
 //asynchronous: register callback function, then send the request.
 var crmServiceObject = this;
 xmlhttp.onreadystatechange = function () {
 fInternalCallback.call(crmServiceObject, xmlhttp, fUserCallback)
 };
 xmlhttp.send(sXml);
 } else {
 //synchronous: send request, then call the callback function directly
 xmlhttp.send(sXml);
 return fInternalCallback.call(this, xmlhttp, null);
 }
 }

FetchUtil.prototype._HandleErrors = function (xmlhttp) {
 /// <summary>(private) Handles xmlhttp errors</summary>
 if (xmlhttp.status != XMLHTTPSUCCESS) {
 var sError = "Error: " + xmlhttp.responseText + " " + xmlhttp.statusText;
 alert(sError);
 return true;
 } else {
 return false;
 }
 }

FetchUtil.prototype.Fetch = function (sFetchXml, fCallback) {
 /// <summary>Execute a FetchXml request. (result is the response XML)</summary>
 /// <param name=”sFetchXml”>fetchxml string</param>
 /// <param name=”fCallback” optional=”true” type=”function”>(Optional) Async callback function if specified. If left null, function is synchronous </param>

var request = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
 request += "<s:Body>";

request += '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services">' + '<request i:type="b:RetrieveMultipleRequest" ' + ' xmlns:b="http://schemas.microsoft.com/xrm/2011/Contracts" ' + ' xmlns:i="http://www.w3.org/2001/XMLSchema-instance">' + '<b:Parameters xmlns:c="http://schemas.datacontract.org/2004/07/System.Collections.Generic">' + '<b:KeyValuePairOfstringanyType>' + '<c:key>Query</c:key>' + '<c:value i:type="b:FetchExpression">' + '<b:Query>';

request += CrmEncodeDecode.CrmXmlEncode(sFetchXml);

request += '</b:Query>' + '</c:value>' + '</b:KeyValuePairOfstringanyType>' + '</b:Parameters>' + '<b:RequestId i:nil="true"/>' + '<b:RequestName>RetrieveMultiple</b:RequestName>' + '</request>' + '</Execute>';

request += '</s:Body></s:Envelope>';

return this._ExecuteRequest(request, "Fetch", this._FetchCallback, fCallback);
 }

FetchUtil.prototype._FetchCallback = function (xmlhttp, callback) {
 ///<summary>(private) Fetch message callback.</summary>
 //xmlhttp must be completed
 if (xmlhttp.readyState != XMLHTTPREADY) {
 return;
 }

//check for server errors
 if (this._HandleErrors(xmlhttp)) {
 return;
 }

var sFetchResult = xmlhttp.responseXML.selectSingleNode("//a:Entities").xml;

var resultDoc = new ActiveXObject("Microsoft.XMLDOM");
 resultDoc.async = false;
 resultDoc.loadXML(sFetchResult);

//parse result xml into array of jsDynamicEntity objects
 var results = new Array(resultDoc.firstChild.childNodes.length);

for (var i = 0; i < resultDoc.firstChild.childNodes.length; i++) {
 var oResultNode = resultDoc.firstChild.childNodes[i];
 var jDE = new jsDynamicEntity();
 var obj = new Object();

for (var j = 0; j < oResultNode.childNodes.length; j++) {
 switch (oResultNode.childNodes[j].baseName) {
 case "Attributes":
 var attr = oResultNode.childNodes[j];

for (var k = 0; k < attr.childNodes.length; k++) {

// Establish the Key for the Attribute
 var sKey = attr.childNodes[k].firstChild.text;
 var sType = '';

// Determine the Type of Attribute value we should expect
 for (var l = 0; l < attr.childNodes[k].childNodes[1].attributes.length; l++) {
 if (attr.childNodes[k].childNodes[1].attributes[l].baseName == 'type') {
 sType = attr.childNodes[k].childNodes[1].attributes[l].text;
 }
 }

switch (sType) {
 case "a:OptionSetValue":
 var entOSV = new jsOptionSetValue();
 entOSV.type = sType;
 entOSV.value = attr.childNodes[k].childNodes[1].text;
 obj[sKey] = entOSV;
 break;

case "a:EntityReference":
 var entRef = new jsEntityReference();
 entRef.type = sType;
 entRef.guid = attr.childNodes[k].childNodes[1].childNodes[0].text;
 entRef.logicalName = attr.childNodes[k].childNodes[1].childNodes[1].text;
 entRef.name = attr.childNodes[k].childNodes[1].childNodes[2].text;
 obj[sKey] = entRef;
 break;

default:
 var entCV = new jsCrmValue();
 entCV.type = sType;
 entCV.value = attr.childNodes[k].childNodes[1].text;
 obj[sKey] = entCV;

break;
 }
 }

jDE.attributes = obj;
 break;

case "Id":
 jDE.guid = oResultNode.childNodes[j].text;
 break;

case "LogicalName":
 jDE.logicalName = oResultNode.childNodes[j].text;
 break;

case "FormattedValues":
 var foVal = oResultNode.childNodes[j];

for (var k = 0; k < foVal.childNodes.length; k++) {
 // Establish the Key, we are going to fill in the formatted value of the already found attribute
 var sKey = foVal.childNodes[k].firstChild.text;

jDE.attributes[sKey].formattedValue = foVal.childNodes[k].childNodes[1].text;
 }
 break;
 }
 }
 results[i] = jDE;
 }

//return entities
 if (callback != null) callback(results);
 else return results;
 }

function jsDynamicEntity(gID, sLogicalName) {
 this.guid = gID;
 this.logicalName = sLogicalName;
 this.attributes = new Object();
 }

function jsCrmValue(sType, sValue) {
 this.type = sType;
 this.value = sValue;
 }

function jsEntityReference(gID, sLogicalName, sName) {
 this.guid = gID;
 this.logicalName = sLogicalName;
 this.name = sName;
 this.type = 'EntityReference';
 }

function jsOptionSetValue(iValue, sFormattedValue) {
 this.value = iValue;
 this.formattedValue = sFormattedValue;
 this.type = 'OptionSetValue';
 }
</script>
</body>
</html>

Dynamics CRM 2011 publish customizations failed after successful import

I have run into this problem before, we can successfully import one solution package but the publish failed with a generic error.

This is because the solution package is large and the SQL Server is slow and someone enabled CRM Server tracing.

I have find a solution from this link: http://blogs.msdn.com/b/darrenliu/archive/2011/07/08/crm-2011-cannot-publish-customizations-after-import.aspx

The cause for this problem is mentioned in that blog: “CRM has a default timeout value of 300 seconds = 5 minutes. If any process takes more than 5 minutes, it’ll stop.”

This is also mentioned in the SDK as well. http://msdn.microsoft.com/en-us/library/gg334495.aspx#BKMK_MaxSizeOfSolution

Change the maximum allowed size by editing the <httpRuntime> element in the web.config file for the application. Edit the executionTimeout and maxRequestLength attributes to allow for the necessary size.

The solution mentioned in the blog will solve the problem as well, but it is a bit outdated.

Dynamics CRM 2011 web.config file use location element, so we can fix the problem by update root web.config file.

One of the fix mentioned in that post might be already fix depend on your Roll Up version.

The complete steps are listed below:

  1. On the CRM application server, open Internet Information Services (IIS) Manager.
  2. Expand the server name, and then expand Web Sites.
  3. Right-click the Microsoft CRM Web site, and then click Open.
  4. Right-click the Web.config file, click Open With, and then click Notepad.
  5. In Notepad, find

<system.web>
 <httpRuntime executionTimeout="300" maxRequestLength="32768" requestValidationMode="3.0" encoderType="Microsoft.Crm.CrmHttpEncoder, Microsoft.Crm" />

change to

 <system.web>
 <httpRuntime executionTimeout="300" maxRequestLength="32768" requestValidationMode="3.0" encoderType="Microsoft.Crm.CrmHttpEncoder, Microsoft.Crm" />

find

 <location path="MSCRMServices">
 <system.web>
 <httpRuntime maxRequestLength="8192" />

change to

 <location path="MSCRMServices">
 <system.web>
 <httpRuntime maxRequestLength="32768" />

Dynamics CRM 2011 update help server url

PowerShell


PS > Add-PSSnapin Microsoft.Crm.PowerShell
 PS > $web = Get-CrmSetting WebAddressSettings
 PS > $web.HelpServerUrl = ""
 PS > Set-CrmSetting $web

C#


var service = Microsoft.Xrm.Sdk.Deployment.Proxy.ProxyClientHelper.CreateClient(new System.Uri("http://crm2011:5555/XRMDeployment/2011/Deployment.svc"));

var webAddressSettings = (Microsoft.Xrm.Sdk.Deployment.WebAddressSettings)service.Retrieve(Microsoft.Xrm.Sdk.Deployment.DeploymentEntityType.WebAddressSettings, null);

webAddressSettings.HelpServerUrl = "http://crm2011:5555";

service.Update(webAddressSettings);

Dynamics CRM 2011 Unit Test Part 10: Microsoft Fakes with workflow activity

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

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

Unit test CRM workflow activity is similar to unit test CRM plugin in terms of the interface we have to mock. There are three distinct points in any code activity unit test, first is how to correctly invoke Execute method since it is protected, second is how to pass in the InArgument parameters and the last is how to pass in required service as extensions.

1. Correctly invoke CodeActivity.Execute(CodeActivityContext executionContext)

Since CodeActivity.Execute(CodeActivityContext executionContext) is protected, the correct way to invoke is method is to construct an instance of System.Activities.WorkflowInvoker and pass in an instance of custom code workflow activity. we can call the Invoke method on the WorkflowInvoker instance to trigger the execution of CodeActivity.Execute method.


AddMemberTeamActivity target = new AddMemberTeamActivity();

var invoker = new WorkflowInvoker(target);

var outputs = invoker.Invoke(inputs);

2. Correctly pass in InArgument<T> properties

the InArgument<T> properties need to be passed in as a dictionary when call WorkflowInvoker.Invoke method


var inputs = new Dictionary<string, object>
 {
 { "User", new EntityReference("systemuser", expectedUserId) },
 { "Team", new EntityReference("team", expectedTeamId) }
 };

var outputs = invoker.Invoke(inputs);

3. Pass in required service as extensions

required service can be added to WorkflowInvoker.Extensions property


var invoker = new WorkflowInvoker(target);
 invoker.Extensions.Add<ITracingService>(() => tracingService);
 invoker.Extensions.Add<IWorkflowContext>(() => workflowContext);
 invoker.Extensions.Add<IOrganizationServiceFactory>(() => factory);

Code Under Test


public sealed class AddMemberTeamActivity : CodeActivity
 {
 [RequiredArgument]
 [Input("User to Add")]
 [ReferenceTarget("systemuser")]
 public InArgument<EntityReference> User { get; set; }

[RequiredArgument]
 [Input("Team to Add To")]
 [ReferenceTarget("team")]
 public InArgument<EntityReference> Team { get; set; }

/// <summary>
 /// Executes the workflow activity.
 /// </summary>
 /// <param name="executionContext">The execution context.</param>
 protected override void Execute(CodeActivityContext executionContext)
 {
 // Create the tracing service
 ITracingService tracingService = executionContext.GetExtension<ITracingService>();

if (tracingService == null)
 {
 throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
 }

tracingService.Trace("Entered AddMemberTeamActivity.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
 executionContext.ActivityInstanceId,
 executionContext.WorkflowInstanceId);

// Create the context
 IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

if (context == null)
 {
 throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
 }

tracingService.Trace("AddMemberTeamActivity.Execute(), Correlation Id: {0}, Initiating User: {1}",
 context.CorrelationId,
 context.InitiatingUserId);

IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
 IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

try
 {
 EntityReference user = this.User.Get(executionContext);
 EntityReference team = this.Team.Get(executionContext);

if (!IsMemberInTeam(service, team.Id, user.Id))
 {
 OrganizationRequest request = new AddMembersTeamRequest { MemberIds = new Guid[] { user.Id }, TeamId = team.Id };

var response = service.Execute(request) as AddMembersTeamResponse;
 }
 }
 catch (FaultException<OrganizationServiceFault> e)
 {
 tracingService.Trace("Exception: {0}", e.ToString());

// Handle the exception.
 throw;
 }

tracingService.Trace("Exiting AddMemberTeamActivity.Execute(), Correlation Id: {0}", context.CorrelationId);
 }

internal bool IsMemberInTeam(IOrganizationService service, Guid teamId, Guid memberId)
 {
 OrganizationServiceContext context = new OrganizationServiceContext(service);

var query = from relationship in context.CreateQuery("teammembership")
 where relationship.GetAttributeValue<Guid>("teamid") == teamId
 && relationship.GetAttributeValue<Guid>("systemuserid") == memberId
 select relationship;

return query.FirstOrDefault() != null;
 }
 }

Unit Test


[TestClass]
 public class AddMemberTeamActivityTest
 {
 [TestMethod]
 public void ExecuteTest()
 {
 //
 // Arrange
 //
 Guid actualUserId = Guid.NewGuid();
 Guid actualTeamId = Guid.NewGuid();
 var workflowUserId = Guid.NewGuid();
 var workflowCorrelationId = Guid.NewGuid();
 var workflowInitiatingUserId = Guid.NewGuid();

// IOrganizationService
 var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();
 service.ExecuteOrganizationRequest = r =>
 {
 AddMembersTeamRequest request = r as AddMembersTeamRequest;
 actualUserId = request.MemberIds[0];
 actualTeamId = request.TeamId;
 return new AddMembersTeamResponse();
 };

// IWorkflowContext
 var workflowContext = new Microsoft.Xrm.Sdk.Workflow.Fakes.StubIWorkflowContext();
 workflowContext.UserIdGet = () =>
 {
 return workflowUserId;
 };
 workflowContext.CorrelationIdGet = () =>
 {
 return workflowCorrelationId;
 };
 workflowContext.InitiatingUserIdGet = () =>
 {
 return workflowInitiatingUserId;
 };

// ITracingService
 var tracingService = new Microsoft.Xrm.Sdk.Fakes.StubITracingService();
 tracingService.TraceStringObjectArray = (f, o) =>
 {
 Debug.WriteLine(f, o);
 };

// IOrganizationServiceFactory
 var factory = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationServiceFactory();
 factory.CreateOrganizationServiceNullableOfGuid = id =>
 {
 return service;
 };

var expectedUserId = Guid.NewGuid();
 var expectedTeamId = Guid.NewGuid();

AddMemberTeamActivity target = new AddMemberTeamActivity();
 using (ShimsContext.Create())
 {
 var fakeTarget = new DynamicsCRMUnitTest.Workflow.Fakes.ShimAddMemberTeamActivity(target);
 fakeTarget.IsMemberInTeamIOrganizationServiceGuidGuid = (svc, teamId, memberId) =>
 {
 return false;
 };

var invoker = new WorkflowInvoker(target);
 invoker.Extensions.Add<ITracingService>(() => tracingService);
 invoker.Extensions.Add<IWorkflowContext>(() => workflowContext);
 invoker.Extensions.Add<IOrganizationServiceFactory>(() => factory);

var inputs = new Dictionary<string, object>
 {
 { "User", new EntityReference("systemuser", expectedUserId) },
 { "Team", new EntityReference("team", expectedTeamId) }
 };

//
 // Act
 //
 var outputs = invoker.Invoke(inputs);
 }

//
 // Assert
 //
 Assert.AreEqual(expectedUserId, actualUserId);
 Assert.AreEqual(expectedTeamId, actualTeamId);
 }

[TestMethod]
 public void IsMemberInTeamTest()
 {
 //
 // Arrange
 //
 Guid userId = Guid.NewGuid();
 Guid teamId = Guid.NewGuid();
 bool expected = true;

AddMemberTeamActivity target = new AddMemberTeamActivity();

// IOrganizationService
 var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();
 service.ExecuteOrganizationRequest = r =>
 {
 List<Entity> entities = new List<Entity>
 {
 new Entity()
 };

var response = new RetrieveMultipleResponse
 {
 Results = new ParameterCollection
 {
 { "EntityCollection", new EntityCollection(entities) }
 }
 };

return response;
 };

//
 // Act
 //
 var actual = target.IsMemberInTeam(service, teamId, userId);

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

Dynamics CRM 2011 Unit Test Part 9: Microsoft Fakes with CRM plugin

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

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

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


[TestClass]
 public class PluginTest
 {
 /// <summary>
 ///A test for Execute
 ///</summary>
 [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 = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();
 service.RetrieveStringGuidColumnSet = (entityName, id, columns) =>
 {
 return new Microsoft.Xrm.Sdk.Entity("account")
 {
 Id = accountId,
 Attributes = { { "numberofemployees", previousNumber } }
 };
 };
 service.UpdateEntity = (entity) =>
 {
 actual = entity.GetAttributeValue<int>("numberofemployees");
 };

// IPluginExecutionContext
 var pluginExecutionContext = new Microsoft.Xrm.Sdk.Fakes.StubIPluginExecutionContext();
 pluginExecutionContext.StageGet = () => { return 40; };
 pluginExecutionContext.MessageNameGet = () => { return "Create"; };
 pluginExecutionContext.PrimaryEntityNameGet = () => { return "contact"; };
 pluginExecutionContext.PostEntityImagesGet = () =>
 {
 return new EntityImageCollection
 {
 { "PostCreateImage", new Microsoft.Xrm.Sdk.Entity("contact")
 {
 Attributes = { { "parentcustomerid", new EntityReference("account", accountId) } }
 }
 }
 };
 };

// ITracingService
 var tracingService = new Microsoft.Xrm.Sdk.Fakes.StubITracingService();
 tracingService.TraceStringObjectArray = (f, o) =>
 {
 Debug.WriteLine(f, o);
 };

// IOrganizationServiceFactory
 var factory = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationServiceFactory();
 factory.CreateOrganizationServiceNullableOfGuid = id =>
 {
 return service;
 };

// IServiceProvider
 var serviceProvider = new System.Fakes.StubIServiceProvider();
 serviceProvider.GetServiceType = 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);
 }
 }