Using jQuery in CRM 2011

With the introduction of Web Resources to CRM 2011, the model for client-side scripting changes completely – in the past we were restricted to embedding chunks of Javascript into the CRM Database to enable certain client side actions, and beyond invoking the CRM Document Object Model for certain CRM-related Functions and Properties, these chunks of Javascript were completely stand-alone lacking any easy ability to include other JS libraries or common functions.

The ability to include Web Resources in a CRM 2011 Solution change this – we are now able to embed Script Libraries into the CRM Database to be used across different events in the solution or potentially by other Web Resources – such that the following is invoke/include structure is possible:

Web Resource 1
function displayLookupName (lookupName, lookup) {
  var displayValueId = “Blank”;
  var displayValueName = “Blank”;
  if ( lookup != null ) {
    displayValueId = lookup[0].id;
    displayValueName = lookup[0].name;
  }
  alert("Display " + lookupName + " Lookup Name Dialogue: " + displayValueName + " : " + displayValueId + "");
}
Web Resource 2
function getLookupProperty( lookupAttributeName ) {
  var lookup;
  lookup = Xrm.Page.getAttribute(lookupAttributeName).getValue();
  displayLookupName(lookupAttributeName , lookup);
}
Event in CRM
getLookupProperty ("parentcustomerid");
CRM2011 Scripting Flow Diagram

Simple diagram showing the execution flow from the User to the Dynamics CRM Platform calling out to the 2 Web Resources in turn

This models more traditional software development where we often have code calling out to other classes or references.  Other than the possible fear of Spaghetti Coding this gives us a far greater level of flexible in how we add scripting into Dynamics CRM – and helps us maintain coding standards to avoid duplicating pieces of common scripting which was often common in CRM 3 and 4 projects.

Incorporating jQuery as a Web Resource

The major benefit of calling out to other classes or scripting is the ability to reuse ‘black-box’ solutions of well tested functionality where we do not need to worry about how the class or script is working, only that it does work and we can use this to achieve the results we are looking for.  This concept of working at a higher level of abstraction is common across software development when we are almost always working at higher levels of abstraction to avoid the lower-levels of complexity we are not interested in.

The concept of using Web Resources as shared Libraries across a CRM Solution allows us to consider including such common ‘black-box’ shared Web Libraries into CRM for use in our custom scripting.  jQuery is one of the more common libraries that has been refined over a period of time and is used in many Web Applications (and indeed is now included by default in NET 4 Web Projects) to provide a common set of functions for making service calls to minimise the about of scripting required.

jQuery Solution in CRM 2011

Adding a Solution to a CRM 2011 Deployment that contains the jQuery and JSON Scripting files as Web Resources

The functions within the jQuery library can then be used to easily invoke the CRM 2011 WCF Services via AJAX calls to the REST End-Points without having to develop lengthy scripting to generate the XML Messages manually.

CRM2011 Scripting Flow Diagram using jQuery

Example Execution path for a Custom Web Resource that uses jQuery to retrieve data from the CRM Organisation Data Service to present back to the CRM User

The link below can be used to download a Managed CRM 2011 Solution containing these jScript and JSON2 Websources:

http://cid-0f98a78f1c3c4457.office.live.com/embedicon.aspx/.Public/jQueryCRM2011_1_6_0_0_managed.zip

Or we can download the scripting files and include them manually:

To compare this approach of retrieving data back from CRM Service using this method of invoking the REST Endpoint via JSON against SOAP XML Messages, the two script examples below illustrate how to retrieve the Account Number for a selected Account in CRM –  the first generating the XML Messages manually and the second using jQuery functions to generate the calls.

SOAP Method: SOAP Call to retrieve the Account Number

constructRetrieveRequestXML = function (EntityId, EntityType, returnfieldName) {
    var xml = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
    xml += "<s:Body>";
    xml += "<Retrieve xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\"";
    xml += " xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
    xml += "<entityName>" + EntityType + "</entityName>";
    xml += "<id>" + EntityId + "</id>";

    xml += "<columnSet xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">";
    xml += "<a:AllColumns>false</a:AllColumns>";
    xml += "<a:Columns xmlns:b=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">";
    xml += "<b:string>" + returnfieldName + "</b:string>";
    xml += "</a:Columns>";
    xml += "</columnSet>";

    xml += "</Retrieve>";
    xml += "</s:Body>";
    xml += "</s:Envelope>";

    return xml;
}

retrieveRequest = function (xml, serverUrl, successCallback) {
    var xmlHttpRequest = new XMLHttpRequest();
    xmlHttpRequest.open("POST", serverUrl, true)
    xmlHttpRequest.setRequestHeader("Accept", "application/xml, text/xml, */*");
    xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    xmlHttpRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Retrieve");
    xmlHttpRequest.onreadystatechange = function () { retrieveResponse(xmlHttpRequest, successCallback); };
    xmlHttpRequest.send(xml);
}

getError = function (faultXml) {
    ///<summary>
    /// Parses the WCF fault returned in the event of an error.
    ///</summary>
    ///<param name="faultXml" Type="XML">
    /// The responseXML property of the XMLHttpRequest response.
    ///</param>
    var errorMessage = "Unknown Error (Unable to parse the fault)";
    if (typeof faultXml == "object") {
        try {
            var bodyNode = faultXml.firstChild.firstChild;
            //Retrieve the fault node
            for (var i = 0; i < bodyNode.childNodes.length; i++) {
                var node = bodyNode.childNodes[i];

                //NOTE: This comparison does not handle the case where the XML namespace changes
                if ("s:Fault" == node.nodeName) {
                    for (var j = 0; j < node.childNodes.length; j++) {
                        var faultStringNode = node.childNodes[j];
                        if ("faultstring" == faultStringNode.nodeName) {
                            errorMessage = faultStringNode.text;
                            break;
                        }
                    }
                    break;
                }
            }
        }
        catch (e) { };
    }
    return new Error(errorMessage);
}

getServerUrl = function () {
    ///<summary>
    /// Returns the URL for the SOAP endpoint using the context information available in the form
    /// or HTML Web resource.
    ///</summary
    var OrgServicePath = "/XRMServices/2011/Organization.svc/web";
    var serverUrl = "";
    if (typeof GetGlobalContext == "function") {
        var context = GetGlobalContext();
        serverUrl = context.getServerUrl();
    }
    else {
        if (typeof Xrm.Page.context == "object") {
            serverUrl = Xrm.Page.context.getServerUrl();
        }
        else
        { throw new Error("Unable to access the server URL"); }
    }
    if (serverUrl.match(/\/$/)) {
        serverUrl = serverUrl.substring(0, serverUrl.length - 1);
    }
    return serverUrl + OrgServicePath;
}

retrieveResponse = function (resp, successCallback) {
    if (resp.readyState == 4) { // complete
        if (resp.status == 200) {
            //Success
            successCallback(resp.responseXML);
        }
        else {
            //Failure
            alert("error! - " + getError(resp.responseXML).description);
        }
    }
}

function parentAccountChangedSOAP() {
    var parentcustomerid = Xrm.Page.getAttribute("parentcustomerid").getValue();

    if (parentcustomerid != null) {
        if (parentcustomerid[0].type == 1) // check that the Customer Relationship is to an Account
        {
            var accountId = parentcustomerid[0].id;

            var xml = constructRetrieveRequestXML(accountId, "account", "accountnumber");

            var processAccountNumber = function (responseXml) {
                try {
                    var accno = null;
                    var pairNames = responseXml.selectNodes("//b:key");
                    var pairValues = responseXml.selectNodes("//b:value");

                    if (pairNames != null) {
                        for (var i = 0; i != pairNames.length; i++) {
                            if (pairNames[i].text == "accountnumber") {
                                accno = pairValues[i].text;
                            }
                        }
                    }

                    if (accno != null) {
                        alert("Account Number: " + accno);
                    }
                    else {
                        alert("No Account Number could be determined");
                    }
                }
                catch (ex) {
                    alert("Process Account Number: " + ex.description);
                }
            };

            retrieveRequest(xml, getServerUrl(), processAccountNumber);
        }
    }
}

Using the traditional SOAP XML is similar to how we could use Javascript to send XML Messages to the CRM 4 Webservice, however these XML Messages are now been passed to the CRM 2011 WCF Service so the syntax differs slightly. (indeed it is possible to use legacy versions of the CRM 4 Webservices schema within CRM 2011 via http://crm2011/mscrmservices/2007/CrmService.asmx for scripting backwards compatibility between the versions.)

We could then attach this Web Resource to the Contact Form in CRM and invoke the parentAccountChangedSOAP method on the onChange event of the Parent Customer field:

SOAP Method

Attaching the SOAP script method via a Web Resource to the onChange event of the Parent Customer field within the Contact Form

jQuery AJAX Call to retrieve the Account Number

function parentAccountChanged() {
    var parentcustomerid = Xrm.Page.getAttribute("parentcustomerid").getValue();

    if (parentcustomerid != null) {
        if (parentcustomerid[0].type == 1) // check that the Customer Relationship is to an Account
        {
            var accountId = parentcustomerid[0].id;
            var accountNumber = getAccountNumber(accountId);
        }
    }
}

function getAccountNumber(accountId) {
    var entity = "Account";
    var select = "?$select=AccountId,AccountNumber"
    var oDataSelect;

    // build query string
    oDataSelect = "/XRMServices/2011/OrganizationData.svc/" + entity + "Set(guid'" + accountId + "')" + select + "";

    $.ajax({
        type: "GET",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: oDataSelect,
        beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },
        success: function (data, textStatus, XmlHttpRequest) {
            // Use for a single selected entity
            ProcessReturnMsg(data.d);
        },
        error: function (xmlHttpRequest, textStatus, errorThrown) {
            alert("Status: " + textStatus + "; ErrorThrown: " + errorThrown);
        }
    });
}

function ProcessReturnMsg(accountEntity) {
    // simply show the Account Number on-screen
    alert("Account Number: " + eval(accountEntity.AccountNumber));
}

In a similar fashion we can then associate this Web Resource to the Contact Form in CRM and invoke the ‘parentAccountChanged’ method to a particular event – in this case the Parent Customer onChange event.  The only difference here is the added requirement to associate the jQuery and JSON2 Web Resources as well:

jQuery JSON Method

Attaching the jScript method and relevant Web Resources to the onChange event of the Parent Customer field on the Contact Form

This second version of the scripting is much shorter and therefore can produce more readable script – whilst the scripts are essentially performing the same function (although against different service end-points in CRM 2011), the inclusion of the jQuery library as a Web Resource allows us to re-use the functions to vastly shorter our script for invoking the service, which allows us to focus on the business logic of the script as opposed to parsing XML files.

The following page in the CRM 2011 SDK has further details on using jQuery and JSON within CRM, and sample Web Resources and scripting.

NOTE: However certain aspects of invoking the WCF OrganizationData Service have their own ‘gotchas’ – see the list at the end of this article..

Using jQuery on the Opportunity Product Form

To use this concept of jQuery calls in CRM 2011 for a practical example, we can develop a simple script to automatically populate the ‘Unit of Measure’ field when selecting a Product for an Opportunity Product or Order Product – a simple customisation that I have found useful for improving the usability of CRM.

We can add the following script to a Web Resource that invokes the jQuery library to call the CRM 2011 REST Endpoint and retrieve fields from the selected Product record.

function productChanged() {
    var productid = Xrm.Page.getAttribute("productid").getValue();

    if (productid != null) {
        retrieveSelectedProduct(productid[0].id);
    }
}

function retrieveSelectedProduct(productid) {
    var entity = "Product";
    var select = "?$select=ProductId,DefaultUoMId,ProductNumber"
    var oDataSelect;

    // build query string
    oDataSelect = "/XRMServices/2011/OrganizationData.svc/" + entity + "Set(guid'" + productid + "')" + select + "";

    $.ajax({
        type: "GET",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: oDataSelect,
        beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },
        success: function (data, textStatus, XmlHttpRequest) {
            // Use for a single selected entity
            ProcessReturnMsg(data.d);
        },
        error: function (xmlHttpRequest, textStatus, errorThrown) {
            alert("Status: " + textStatus + "; ErrorThrown: " + errorThrown);
        }
    });
}

function ProcessReturnMsg(productEntity) {
    if (productEntity.DefaultUoMId != null) {
        var uomId = productEntity.DefaultUoMId.Id;
        var uomName = productEntity.DefaultUoMId.Name;

        // insert the default unit of measure
        var defaultuomid = new Array();
        defaultuomid[0] = new Object();
        defaultuomid[0].id = uomId;
        defaultuomid[0].name = uomName;
        defaultuomid[0].entityType = "uom";

        Xrm.Page.getAttribute("uomid").setValue(defaultuomid);
    }
}

We can then invoke this script from the Order Product form for the Product field’s onChange event.

Adding the Script to the Order Product Form

Referencing the relevant Web Resources (including jQuery and JSON that the Default Unit of Measure depends upon) to the Order Product Form in CRM 2011

This is essentially a CRM 2011 + jQuery version of this script which performed the same function in CRM 4.  Scripts that were used in CRM 4 for retrieving information back from the CRM Webservices can be updated in the same fashion.

Conclusion

This concept of adding various Web Resources to work at a higher level of abstraction will likely become wide-spread in CRM 2011 Development Projects – and as jQuery allows us to make simpler script calls to Read or Update CRM Calls in a supported fashion that is well documented in the SDK, it is likely to be a corner stone of CRM Development in future and so is well worth any aspiring CRM Developer being familiar with the technique, and CRM Consultants aware of how this affects Requirements and Design of solutions using 2011.

Gotcha’s and jQuery Reference

The CRM 2011 Organization Data Service is case sensitive for working with jQuery, so when working with scripts we always need to supply the correct Schema Name for fields that CRM expects.

To look up what these schema are, we can examine the Fields area for the entity:

image

Or download the Organization Data Service WSDL file to find how the field has been capitalised:

image

So the following script will not function correctly:

    var entity = "new_discountitem";
    var select = "?$select=new_discountitemid,new_name"
    var oDataSelect = "/XRMServices/2011/OrganizationData.svc/" + entity + "Set(guid'" + productid + "')" + select + "";

Whilst the following will:

    var entity = "new_discountitem";
    var select = "?$select=new_discountitemId,new_name"
    var oDataSelect = "/XRMServices/2011/OrganizationData.svc/" + entity + "Set(guid'" + productid + "')" + select + "";

This can be a bit of gotcha for developers who have been working with previous versions of CRM as previously Schema Names could always be thought of as all lower-case, however this no longer the case in 2011.

Posted in CRM 2011, Development, JavaScript | Tagged , , , | 13 Comments

Using LINQ in CRM 2011 Plugins

One of the technical points that CRM 2011 has introduced is the ability to use LINQ queries to invoke the CRM Service as opposed to the traditional Execute methods or FetchXML.

This allows the use of both simple LINQ queries to access data within CRM, and also the ability to use typed methods at design-time that combines with Visual Studio IntelliSense to make developing code easier.

This is a subject that this blog has touched upon before when the method was made available in one of the later CRM 4 SDK Releases – however I thought this topic was worth revisiting in the context of CRM 2011, and seeing how we can incorporate LINQ into Plugins.

To break this article down, this fits into two sections:

1. Generating Typed Code for invoking the CRM Service via LINQ
2. Using the Generated Code within CRM 2011 Plugins

Addendum – Registering Device ID

Generating Typed Code for invoking the CRM Service via LINQ

The CrmSvcUtil tool is provided as part of the CRM 2011 SDK for referencing the CRM Services and generating a set of code classes based on the Service WSDL which can then allow for typed design-time LINQ queries in Visual Studio.

(this is obviously similar to the Visual Studio SvcUtil tool for generating code from other Service WSDL files)

The CrmSvcUtil can be found in the Bin folder of the CRM SDK:


image

The CrmSvcUtil within the BIN folder of the CRM 2011 SDK

This utility can be run to generate a set of code files from which you can invoke the CRM 2011 WCF Service, to do this we can run the executable from the command line with the following parameters:

crmsvcutil 
  /url:"[x]/XRMServices/2011/Organization.svc" 
  /username:"[y]" /password:"[z]" 
  /language:cs /namespace:DevCrm /out:"DevCrm.cs" /serviceContextName:Xrm

The values for [x], [y] and [z] then depend on your CRM 2011 Deployment method:


[x] [y] [z]
CRM Online Https Url of your CRM Online Organisation. Your Windows Live Username Your Windows Live Password
On-Premise Http or Https Url of your CRM Server and Organisation Your Domain Username Your Domain Password
IFD Deployment Http or Https Url of the Internet Facing CRM Server for your Organisation Your IFD Username Your IFD Password

NOTE: You can also run this command using a config file as opposed to including all the various parameters in the command line.

Documentation on the other parameters can be found in the CRM 2011 SDK. In some cases for On-Premise or IFD Deployments a Device ID and Password may also be required, see the addendum at the end of this post for more details.

From running the CrmSrvUtil tool, this will produce a large CS/VB file containing code for invoking the CRM 2011 Webservice using typed methods and classes – such that these types will include any custom entities or attributes that exist within the CRM Deployment we have used to generate the code:


image

Viewing the code generated by the CrmSvcUtil tool in Visual Studio

We can then use these types in different projects as the method of connecting the project to the CRM Service.

Using the Generated Code within CRM 2011 Plugins

If we using this code in an external project (say for a separate Web Application or a traditional Console Application) we could compile the code as a separate DLL and then reference this DLL to keep the code separate – almost as a Data Access Layer and a separate Business Logic layer.


image

Compiling the code produced by the CrmSvcUtil in a separate Project to produce a DLL that our Plugin can then reference separately – noting here that both the Microsoft.Xrm.Sdk and System.Runtime.Serialization DLLs must be referenced here to accomplish this.

However in this example of developing a Plugin, the project needs to be self contained for registration to the CRM Database so we instead add the code file to our new Plugin Project directly:

image

Authoring a new Plugin Project in Visual Studio including the CrmSvcUtil generated code

With the generated DevCrm.cs code included in the project, we can then begin building the Plugin logic itself and add the usual code to create a reference to the CRM 2011 Service from the Plugin Context as normal for invoking requests to the service:

IPluginExecutionContext context = (IPluginExecutionContext) serviceProvider.GetService(typeof(IPluginExecutionContext));
// get reference to CRM Web Service
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

However we can then add a further reference to invoke the typed code we have produced from our CRM Deployment via the CrmSvcUtil tool:

 
// Use reference to invoke pre-built types service 
DevCrm.Xrm myCrmService = new DevCrm.Xrm(service); 

This then allows us to reference the CRM Service using typed LINQ methods as opposed to untyped object references – the following table compares a method of counting the number of Opportunities that are associated to a particular Account in CRM, showing the code without the LINQ methods and code with LINQ:

Untyped Object Reference (Without LINQ)

QueryByAttribute queryByAttribute = new QueryByAttribute("opportunity");
queryByAttribute.ColumnSet = new ColumnSet(new string[] { "opportunityid" });
queryByAttribute.Attributes.Add("customerid");
queryByAttribute.Values.Add(accountId);
EntityCollection retrieved = service.RetrieveMultiple(queryByAttribute);
return retrieved.Entities.Count;

Typed LINQ Method

var opportunities = myCrmService.OpportunitySet.Where(i => i.CustomerId.Id == accountId);
return opportunities.ToList().Count;

NOTE: Here we use .ToList().Count to determine the number of returned records from the the Select statement, as the LINQ Count and Sum operations are not supported for using LINQ with CRM 2011.

The LINQ Methods then help to reduce the amount of lines of code and hopefully ensure more human-readable code, particularly as Visual Studio will then also provide IntelliSense when working with the different (possibly customised) CRM Attributes and Entities:


image

Building a LINQ query in Visual Studio using IntelliSense via the typed Classes and Methods generated by the CrmSvcUtil tool for a particular deployment of CRM 2011

This naturally makes invoking multiple queries and searches easier than looking up different Attribute Names, State Codes, Picklists and other values.

To look at using this in practise, we can build a similar plugin to one detailed in this blog’s earlier post concerning Plugins in CRM 2011 with the difference of invoking the CRM 2011 service via typed LINQ methods:

    public class CRM2011Plugin : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            try
            {
                IPluginExecutionContext context = (IPluginExecutionContext) serviceProvider.GetService(typeof(IPluginExecutionContext));
                Entity opportunityProductEntityImage;
                bool isDelete = false;
                Guid opportunityProductId = Guid.Empty;
                
                #region Get Opportunity Product Entity Image
                if (context.MessageName == "Create")
                {
                    // use Post Image for Create Messages
                    if (context.PostEntityImages.Contains("PostImage") &&
                        context.PostEntityImages["PostImage"] is Entity)
                    {
                        opportunityProductEntityImage = (Entity)context.PostEntityImages["PostImage"];
                    }
                    else
                    {
                        throw new Exception("No Post Image Entity in Plugin Context for Create Message");
                    }
                }
                else if (context.MessageName == "Update")
                {
                    // use Post Image for Create Messages
                    if (context.PostEntityImages.Contains("PostImage") &&
                        context.PostEntityImages["PostImage"] is Entity)
                    {
                        opportunityProductEntityImage = (Entity)context.PostEntityImages["PostImage"];
                    }
                    else
                    {
                        throw new Exception("No Post Image Entity in Plugin Context for Create Message");
                    }
                }
                else if (context.MessageName == "Delete")
                {
                    // use Pre Image for Delete Messages
                    if (context.PreEntityImages.Contains("PreImage") &&
                        context.PreEntityImages["PreImage"] is Entity)
                    {
                        opportunityProductEntityImage = (Entity)context.PreEntityImages["PreImage"];
                        isDelete = true;
                    }
                    else
                    {
                        throw new Exception("No Pre Image Entity in Plugin Context for Delete Message");
                    }
                }
                else
                {
                    // Plugin not valid for any other Message Types
                    throw new Exception("SumOpportunityProducts Plugin is not valid for the '" + context.MessageName + "' message type");
                }

                if (opportunityProductEntityImage.LogicalName != "opportunityproduct")
                {
                    // plugin is valid for this entity
                    throw new Exception("SumOpportunityProducts Plugin is not valid for the '" + opportunityProductEntityImage.LogicalName + "' entity type");
                }
                #endregion

                #region Plugin Business Logic

                if (isDelete == true)
                {
                    opportunityProductId = opportunityProductEntityImage.Id;
                }

                // Opportunity Product must have a reference to an Opportunity
                if ( opportunityProductEntityImage.Attributes.Contains("opportunityid"))
                {
                    EntityReference opportunityId = (EntityReference) opportunityProductEntityImage.Attributes["opportunityid"];
                    // get reference to CRM Web Service
                    IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                    IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
                    // Use reference to invoke pre-built types service
                    DevCrm.Xrm myCrmService = new DevCrm.Xrm(service);

                    decimal opportunityProductsTotalValue = 0;

                    // get value of total Opportunity Products
                    var opportunityProducts = myCrmService.OpportunityProductSet
                        // conditions
                        .Where(i => i.OpportunityId.Id == opportunityId.Id)
                        // if a PreState Delete Operation - do not include the Opportunity Product we are deleting!
                        .Where(i => i.Id != opportunityProductId)
                        // operation
                        .Select(i => new { i.Id, i.ExtendedAmount });

                    foreach (var op in opportunityProducts)
                    {
                        if (op.ExtendedAmount != null)
                        {
                            opportunityProductsTotalValue += op.ExtendedAmount.Value;
                        }
                    }

                    // get Opportunity
                    var associatedOpportunity = myCrmService.OpportunitySet.FirstOrDefault(i => i.Id == opportunityId.Id);

                    associatedOpportunity.new_customamountfield = new Money();                    
                    associatedOpportunity.new_customamountfield.Value = opportunityProductsTotalValue;

                    // flag the opportunity as being updated
                    myCrmService.UpdateObject(associatedOpportunity);

                    // save changes made via the LINQ queries
                    myCrmService.SaveChanges();                    
                }
                else
                {
                    throw new Exception("Opportunity Product does not have an association back to an Opportunity");
                }

                #endregion
            }
            catch (Exception ex)
            {
                throw new InvalidPluginExecutionException("LINQPlugin.SumOpportunityProducts --> Execute'" + ex.Message + "'");
            }
        }
    }

This simple plugin uses this LINQ method of invoking the CRM Service to sum the total value of Opportunity Products to a custom field on the Opportunity – essentially duplicating CRM’s native functionality to tally the Opportunity Products.

This would be useful if we were creating a Plugin to tally a custom entity associated to the Opportunity, or had added other custom fields to the Opportunity Product entity which we needed to be tallied on the parent Opportunity – however here this serves as a simple example of how we could use LINQ in CRM 2011 Plugins, and how this can reduce the volume of code required for our Business Logic.

To Register this Plugin, we would then compile and run the Plugin Registration tool and register the DLL against the relevant steps required – in this instance PostCreate, PostUpdate and PreDelete of the OpportunityProduct entity:


image

Registering the LINQ Plugin into CRM 2011 against the Opportunity Product entity with PostCreate, PostUpdate and PreDelete steps

As often with Plugins – it can be better to test the code using the Synchronous steps as opposed to the Asynchronous steps as these will display errors on-screen as opposed to the being hidden within the CRM Asynchronous service.

This concept of using LINQ queries as opposed to manually building Fetch XML in the form of QueryExpression and Execute messages is a useful technique for managing custom code – it is more suited for large code bases and is often used for the CRM Portal Projects, however it is also useful within Plugins or Plugin Projects where we want to incorporate quick queries to and from CRM.

Further Reading

  • The following post is from the Dynamics CRM Team Blog and provides a download to a LINQPad tool that can be used to test LINQ Queries against a deployment of CRM 2011, and translate LINQ Queries into Fetch XML – useful for testing LINQ statements before using them in code projects:

    http://blogs.msdn.com/b/crm/archive/2011/01/11/a-better-way-to-learn-linq-to-crm-linqpad-plugin-for-ms-crm-2011-is-available.aspx

  • This blog post from Pogo69 gives a very comprehensive overview of LINQ for CRM 4, most of which is equally applicable for using LINQ with CRM 2011.

    Pogo69, The Missing LINQ CRM 4.0 SDK Advanced Developer Extensions

  • Addendum – Registering Device ID

    Often when working with the CrmSvcUtil we may need to register the Development Workstation as a device, and receive a Device ID and Password – this is not required for use when using a CRM Online deployment as the CrmSvcUtil will automatically register your development workstation, however can be required for On-Premise or IFD Deployments.

    The SDK contains a Code Project titled DeviceRegistration – in order to obtain a DeviceId and DevicePassword that the CrmSrvUtil tool can require, we must compile this project and run the executable that is produced.

    NOTE: We do not need to worry about any of the code in this project, we simply need to compile the code as-is from the SDK.


    image

    The Device Registration Project within the CRM 2011 SDK


    image

    Compiling the Device Registration Tool in Visual Studio

    This will then either register your workstation for a Device ID and Password – or if the device is already registered, inform you of your device id and password.

    image

    Running the compiled Registration Tool to generate a unique Device ID and Password

    We can then invoke this Device ID and Password when running the CrmSvcUtil tool included in the SDK.

    Posted in CRM 2011, Development, LINQ, Technical, xRM | Tagged , , | 9 Comments

    Filtered Lookups in CRM 2011

    Often when working with CRM Projects we can see requirements where we require a selection of other CRM records filtered to only records that are associated with an earlier user selection.  A common example would be selecting a key contact or finance contact for an Opportunity or Order, but other examples of this could often be seen across different project requirements – say for selecting a Property from within a particular Portfolio, or selecting a particular Order to Invoice that related to a specific Customer Account.

    In earlier posts on this blog I have looked at different approaches that could be used to implement a filtered lookup for CRM 4, this article aims to show how we can implement simpler solutions using the new functionality in CRM 2011.

    Earlier Posts concerning Filtered Lookups within CRM 4
    Filtered Lookup as a Drop-down Picklist in MSCRM
    Unsupported Method for Filtered Lookup in MSCRM 4.0

    Initially this post will look at the simpler and more best practise method for implementing a Filtered Lookup through the now standard CRM 2011 customisation options, and then take a look at a more complex Form Scripting method of achieving the same result.

    Filtered Lookups via Simple Customisation in CRM 2011

    When adding a Lookup field to a Form in CRM 3 or 4 we were restricted to a specific ‘Lookup View’ that defined how records of that particular entity would appear within the lookup – this was quite restrictive and forced us to have a fixed set of fields for all Lookups to a certain entity type across a MSCRM Solution.

    CRM 2011 attempts to resolve this problem by offering us options to set both:

    image

    Configuring the properties for a Lookup control in CRM 2011

  • Selecting which view to use to display possible Lookup selections, as well as controlling which Views the CRM User is allowed to select from.
  • Selecting a filter to show only records that match a certain dynamic criteria, with the option to control whether the User is allowed to remove the Filter or not.
  • This allows us to control how the Lookup will appear in CRM 2011 through simple customisation, and so avoid developing any code to produce a Filtered Lookup.

    So in the example above – we would facilitate a Lookup which would display the same fields as the ‘Active Contacts’ view, and crucially, would only display Contacts where:

    [CONTACT].[(Account) PARENT CUSTOMER] = [OPPORTUNITY].[(Account) POTENTIAL CUSTOMER]

    Or in real-world terms, where the Account associated to a Contact is the same as the Account associated to the Opportunity. 

    (on the basis that the Contact’s Parent Customer relationship is with an Account record, and similarly the Opportunity’s Potential Customer relationship is to an Account record.)

    With the result being a useful filtered Lookup which allows the CRM User to choose a particular Point of Contact for the Opportunity from the established list of Contacts associated to the Account:

    image

    Selecting a Contact for a particular Account via the Filtered Lookup

    Complex Filtered Lookups via Form Scripting in CRM 2011

    The example shown above gives the best supported method for adding a Filtered Lookup and is considerable easier than attempting to implement a similar Lookup in CRM 4, however in some instances we may require a more complex Filtering than the Related Records Filtering allows.

    To do this, we can look at incorporating 2011 Form Scripting via a Web Resource to invoke methods in the CRM 2011 Form Document Object Model to manipulate the behaviour of the Lookup fields on the Form.

    Behind the CRM 2011 Forms, the Document Object Model has been significantly changed from CRM 4 to support a series of Xrm methods to both give CRM Developers further control over the Form and Controls through well-defined methods as opposed to direct modifications to the DOM as was the case in CRM 3 and 4.  This then allows for a move towards full support for HTML 5 and therefore multi-browser support. (which is widely expected to be built into the future releases of CRM according to the recent May 2011 Statement of Direction)

    In this vein, these Xrm methods provide us powerful methods for manipulating Lookup Controls to provide an additional method of implementing a filtered lookup.

    addCustomView Adds an entirely new custom view to the Lookup Control.
    setDefaultView Sets the initial default view when a user clicks on the Lookup Control.
    getDefaultView Returns the GUID for the initial view when a user clicks on the Lookup Control.

    (further detail on the Xrm.Page methods can be found at the following page within the CRM 2011 SDK.)

    We can use these methods to build ourselves a simple set of scripting to build and add a custom view to the ‘Point of Contact’ field that will provide the Filtered Lookup:

    var defaultViewId;
    
    // FUNCTION: formOnLoad
    function formOnLoad(accountFieldName, lookupFieldName) {
    
      defaultViewId = Xrm.Page.getControl(lookupFieldName).getDefaultView();
      setLookup(accountFieldName, lookupFieldName, false);
    
    }
    
    // FUNCTION: setLookup
    function setLookup(accountFieldName, lookupFieldName, resetSelection) {
    
        // Get the selected Account Id in the [accountFieldName] indicated control
        var account = Xrm.Page.getAttribute(accountFieldName).getValue();
    
        if ( account != null )
        {
          var accountid = account[0].id;
          var accountname = account[0].name;
    
          if ( resetSelection == true )
          {
            // reset old selection for Contact
            Xrm.Page.getAttribute(lookupFieldName).setValue(null);
          }
    
          // use randomly generated GUID Id for our new view
          var viewId = "{1DFB2B35-B07C-44D1-868D-258DEEAB88E2}";
          var entityName = "contact";
    
          // give the custom view a name
          var viewDisplayName = "Active Contacts for " + accountname + "";
    
          // find all contacts where [Parent Customer] = [Account indicated by AccountId]
          // AND where [Statecode] = Active
          var fetchXml = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" +
                         "<entity name='contact'>" +
                         "<attribute name='firstname' />" +
                         "<attribute name='lastname' />" +
                         "<attribute name='contactid' />" +
                         "<order attribute='lastname' descending='false' />" +
                         "<filter type='and'>" +
                         "<condition attribute='parentcustomerid' operator='eq' value='" + accountid + "' />" +
                         "<condition attribute='statecode' operator='eq' value='0' />" +
                         "</filter>" +
                         "</entity>" +
                         "</fetch>";
    
          // build Grid Layout
          var layoutXml = "<grid name='resultset' " +
                                   "object='1' " +
                                   "jump='contactid' " +
                                   "select='1' " +
                                   "icon='1' " +
                                   "preview='1'>" +
                               "<row name='result' " +
                                    "id='contactid'>" +
                                 "<cell name='firstname' " +
                                       "width='200' />" +
                                 "<cell name='lastname' " +
                                       "width='250' />" +
                                 "<cell name='parentcustomerid' " +
                                       "width='250' />" +
                               "</row>" +
                             "</grid>";
    
          // add the Custom View to the indicated [lookupFieldName] Control
          Xrm.Page.getControl(lookupFieldName).addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, true);
        }
        else
        {
          // no Account selected, reset Contact Lookup View to the default view such that all Contacts are displayed for selection
          Xrm.Page.getControl(lookupFieldName).setDefaultView(defaultViewId);
        }
    }
    

    With this scripting in place via the Web Resource, we can connect the Form and Field events to invoke the script functions:

    image

    Attaching the ‘setLookup’ event from the Scripting contained in the Web Resource to the Opportunity’s ‘customerid’ field onChange Event

    image

    Attaching the ‘formOnLoad’ event from the Scripting contained in the Web Resource to the Opportunity’s onLoad Event

    Once published, we can open an Opportunity and select a Potential Customer Account to then automatically limit the ‘Point of Contact’ field to just the Active Contacts that are linked to the selected Account:

    image

    Selecting an Account record for the Opportunity’s Potential Customer

    This then acts to limit the user’s selection of a Point of Contact to the relevant Contacts for the Opportunity:

    image

    Selecting a Contact for a particular Account via the Filtered Custom View supplied by Form Scripting”

    In determining how best to cater for these Scripted Filtered Lookups in CRM 2011 I found the following articles and supporting Javascript very useful:

    http://www.harris-tech.com/blogs/bid/54720/Creating-Filtered-Lookups-in-CRM-2011

    http://nishantrana.wordpress.com/2010/12/31/filtered-lookup-in-crm-2011/

    The Managed and Unmanaged Solution XML for this scripted ‘Point of Contact’ customisation to CRM can be downloaded via the following links:

    http://cid-0f98a78f1c3c4457.office.live.com/embedicon.aspx/.Public/PointOfContact_0_0_1_0_Managed.zip

    http://cid-0f98a78f1c3c4457.office.live.com/embedicon.aspx/.Public/PointOfContact_0_0_1_0.zip

    From these steps hopefully this gives a good idea of how Filtered Lookups can be implemented in CRM 2011 in most cases without recourse to development, and how when development is required that this can be significantly reduced and better managed than in earlier versions of CRM – with the scripting required here a good introduction to how the Xrm methods change how we can use Form Scripting in Dynamics CRM.

    The Form Scripting model of CRM 2011 is something I would like to return to in different posts, particularly the changes here from CRM 4 and the ability via Web Resources to incorporate Javascript libraries such as jQuery into Dynamics CRM Solutions to give us more flexibility in scripting as opposed to requiring more traditional server-side .NET coding.

    Posted in Consultancy, CRM 2011, JavaScript | Tagged , , , | 20 Comments

    Solutions in CRM 2011

    One of the common problems using previous versions of Dynamics CRM was managing the various customisations throughout a project, particularly when thinking of developing reusable sets of customisations that could be deployed to multiple CRM Solutions.

    The only method we had was to use a different customisation prefix (from the default new_) for different sets of customisations to manage the different fields and entities we may have wanted to re-use, followed by careful version management for exporting certain entities into separate XML files which could then be imported into other solutions as bite-size components.  This could be done with enough attention and common sense, but made installing, and particularly uninstalling, reusable components into CRM deployments difficult.

    CRM 2011 aims to resolve this problem through the use of Solutions that act to group different sets of customisations into individual components which can then be added or removed to a deployment of CRM 2011 through the customisation area.  This article aims to detail an overview of Solutions in CRM 2011 and a basic example of how these could be used.

    Defining a new Solution in CRM 2011

    Our initial step is to browse to the Solutions section within the Settings area of 2011.

    image

    The Customisation area of the Settings section in 2011

    Here we can see the list of Solutions present in this deployment of CRM 2011, for most deployments this will initially be blank – however we can create a new Solution here that we can use to group new Attributes or Entities we want to add to CRM as a distinct unit.

    image

    The Solutions area of CRM 2011

    We can then provide the Solution a Name, Publisher, Configuration Page and Version.

    Display Name The real-world name for the Solution, with the Logical Name defaulting from this.
    Name The Logical or behind-the-scenes Name for the Solution.
    Publisher The Publisher defines who is authoring and distributing the CRM 2011 Solution.  Initially each CRM 2011 Deployment defaults to a specific publisher for that Deployment, but this can be amended to define a specific Publisher record and description about the Publisher.
    Configuration Page Links to a Web Resource which will act as the Configuration Page for this Solution, such that the Solution has an area for Administrative Users of the Solution to detail any Settings or Connections that may be required for the Solution.
    Version The Version Number given for the Solution, useful when releasing different versions of the same Solution to Customers or Clients.
    image

    Creating a new Solution in CRM 2011

    After defining the Solution, we can then add new Attributes, Entities and other customisation areas to the deployment of CRM 2011 as part of the new Solution.

    Adding Content to the Solution

    Once the Solution has been added to CRM we can add different customisations, or components, as parts of the new solution:

    image

    Adding a new Entity Component to the Solution

    Taking an example of a possible Solution format we could use, we could envisage the scenario where a business runs a number of Hotel Properties; each of which collects information about the standard of the Hotel and feedback on the level of service provided by the staff in the form of a Customer Questionnaire.  This feedback is then collected in the form of a simple Spreadsheet with one row per Questionnaire, as shown below:

    image

    Example Spreadsheet data to import into the solution

    To visualise this scenario, we could create the following customisations as part of a new  2011 Solution to address these requirements:

    • A Custom Option Set for describing the result of a Service Questionnaire Tick between Excellent, Good, Satisfactory and Poor.
    • An additional CSQ Entity to represent a returned Questionnaire.
    • An additional Property Entity to represent the property or location that a returned questionnaire refers to.
    • Additional fields on the CSQ Entity for different ranking scores: Availability, Competence, Attention to Requirements and Overall Impression – each using the Custom Option Set defined.
    • Additional fields on the CSQ Entity to describe the Date that the Questionnaire was returned.
    • Additional N:1 relationship between the CSQ Entity and the Property entity to describe which returned questionnaires relate to which location.
    • Additional N:1 relationship between the CSQ Entity and the Customer who returned the questionnaire. (potentially blank if the Customer completed the Questionnaire anonymously)

    This list of customisations then fulfils our basic requirement to record these Feedback Questionnaires in CRM 2011 – we can then create a Datamap to meet the requirement to import the Questionnaire raw data into CRM without the need for manual entry:

    image

    Configuring the Data Map to import the Spreadsheet CSQ data into CRM 2011 CSQ records

    The Data Import alongside the Solution Customisations then gives us a workable solution to the requirements to import and record the Questionnaire information in CRM 2011.

    image

    The Questionnaire information in CRM 2011 as per the Solution Customisation and the imported data.

    Using this example of a developed solution, we can then think about how we can Export, Import or Remove this Solution from our CRM 2011 Deployment.

    Exporting a Solution as XML

    From here we can then decide how the Solution can be used outside this deployment of CRM 2011, similar in respects to how we could export/import XML files when using CRM 3 or 4, but instead of exporting or importing whole deployments or individual entities here we can export just the solution we have developed itself.

    image

    Exporting the XML for the Solution.

    Here we can export the XML describing the Solution we have created, either encompassing all aspects of the solution’s customisations or simply certain types of customisation or configuration for the solution.

    image

    Choosing which aspects of System Customisation or Configuration to export as XML from the selected Solution.

    The next step is either we want to export the Solution XML as a Managed or Unmanaged package which determines how the Solution can be used when imported into a CRM 2011 Deployment.

    image

    Choosing whether to export the Solution XML as a Managed Solution or an Unmanaged Solution.

    Once we finished the Export Solution Wizard, the XML file can then be downloaded and saved for future use either as a Managed Solution or an Unmanaged Solution.

    Managed Solutions vs Unmanaged Solutions

    The concept of Managed Solutions and Unmanged Solutions is a new option in CRM 2011 to allow us to export our Solution as either a set of flexible customisations that can be further changed or customised in the future when imported into other CRM 2011 Deployments, or as a set of fixed customisations that can only be customised to an extent controlled by the Solution’s original author.

      Unmanaged Solution Managed Solution
    Can be Deleted By removing each Component individually. Removing the Solution will automatically remove all the Components involved.
    Can be Customised Components can be customised. Components can only be customised accordingly to predefined Managed Properties.
    Can be Exported Can be exported to XML files as either a Managed or Unmanaged Solution Cannot be exported

    Therefore when authoring a new Solution, each area of customisation involved in a CRM 2011 Solution will have an option to alter the Managed Properties for that component:

    image

    Control which aspects of an entity in the solution can be changed or customised after importing the solution as part of a Managed Solution.

    This set of Managed Properties then affects the level of customisation or changes that can be made to the Solution when imported into a CRM 2011 Deployment as a Managed Solution.

    Removing a Solution

    The useful aspect about grouping sets of customisations as a Managed Solution is that the Solution can be removed from a CRM 2011 Deployment as a single action.  (whereas Unmanaged Solutions or Customisations in CRM 3 or 4 would need to be deleted component by component)

    image

    Removing a Managed Solution from CRM 2011 to also remove all the Customisation Components involved with the Solution.

    Now when browse to the Service area of our CRM 2011 Deployment – both the CSQ and Properties entities have been removed, as both of these Entity Components were part of the Managed Solution that has been removed/uninstalled.

    Solution Development

    This process of development, export and then import gives us a Solution Development Lifecycle when using CRM 2011 that is similar to the traditional Software Development Lifecycle for developing custom applications – when we can think of the Unmanaged Solution XML as the core Code or Script, and the Managed Solution XML as the compiled production Code.

    Development   Test Production
    Unmanaged Solution   Imported as a Managed Solution Imported as a Managed Solution
    Constructed by Development Team Exported as a Managed Solution Tested as a Managed Solution by the Testing Team Used in Production

    Downloadable Examples

    The following two ZIP file downloads contain the XML for the Solution outlined in this post, both as a Managed version of the Solution and the Unmanaged version.

    Unmanaged Solution XML Managed Solution XML

    Conclusion

    This concept of Solutions goes a long way to resolve the problems that we encountered when building reusable sets of customisation in CRM 3 and 4 – allowing us to use and release almost ‘drag-and-drop’ extensions that we can add or remove into our CRM 2011 Deployments as managed extensions.

    As Consultants and Developers, this gives us a more manageable method of releasing and installing Future Phase Releases or Issue Resolution Releases to our Customers; more generally this can also allow Dynamics Partners and Professionals to offer their solutions as new extended areas of functionality for general release via the Dynamics Marketplace built into the Settings area of CRM 2011.

    image

    Dynamics Marketplace area within CRM 2011.

    image

    An example of a Dynamics CRM Solution or Project for download through the Dynamics Marketplace – built into the Microsoft PinPoint system for managing Partner Releases

    Further information about PinPoint can be found here.

    Posted in Consultancy, CRM 2011, Customisation | Tagged , | 3 Comments

    FAP and what makes a ‘good document’

    For a long time before any software is purchased or code written a software or change management project only exists as a series of documents which lay out what the project is hoping to achieve – as such for CRM projects Consultants and Project Managers will have to write a good number of documents throughout a project. 

    Throughout my time I have a seen a great deal of these documents and come across a common problem in seeing documents that fail to describe what the client is looking for at that point in a project – often when picking up an existing project I am presented with a weighty tome that is the only document which describes what on earth as go before or is hoping to happen in the future; which is fine for Consultants or other people heavily involved in the project but at some point the senior management (and the people controlling the purse-strings!) will quite rightly ask ‘what are we getting for our investment?’ only to be presented with a 300 page document.  It is kind of the equivalent of a meeting where someone is asked a question and responds by talking non-stop for 20 minutes as way of an answer. 

    No one enjoys reading weighty project documents or long technical answers, however we can make these documents (which are in affect an attempt to communicate like any other) more relevant to the intended reader and hope to convey the information we need to convey at that point in the project.  Towards achieving this I tend to think back to the traditional English-Essay style of writing: FAP, Form, Audience, Purpose.

    Form – what is the format of the document?  The general tone and writing style of the document, should the document consist of smaller bullet points that each convey a separate point (a la a requirements catalogue) or should the document be more human readable with strong sentence structures and possible narrative.

    Audience – who is the document aimed at?  Does the Decision Maker of a company who is concerned about whether a project will justify the investment (particularly to Shareholders or the Finance Director) want to read through the low-level detail of Solution Design aimed at the solution developers, in most cases he or she will want a document more tailored to their high-level concerns.  Documents produced throughout a project should know their intended audiences(s and be written accordingly.

    Purpose – what is the document hoping to achieve?  Is there any point in a document detailing a low-level requirements of a software system when the proposed benefits of the system have yet to be confirmed, each document produced throughout a project should have a set of purposes which are ideally set out clearly as the foreword to the document itself.

    As an example, applying this to the different phases of a traditional Waterfall project we can look at the different documentation we may produce at each phase and how we can keep each of the documents ‘on-track’ to be informative and relevant to the intended audience.

    NOTE: This example uses the example of Waterfall style Project Stages that fits into my usual PRINCE2 model of thinking – obviously these stages would vary for other Agile or SureStep methodologies, and also may vary or merge depending on the size of different projects.

    1. Scoping Document

    I would see this as a statement of intent document that outlines the broad problems that the client is looking to solve, the scope of these problems and a very high-level outline of how these problems may be solved with (guess-)estimated costs – often acting as the Project Initiation Document for potential future phases of the project.

    The document should be readable and be kept as brief as possible to give the reader a good feel of the potential project without including low-level detail.

    Based upon: Short schedule of workshop sessions with small groups or individuals who form stakeholders, key decision makers and management for each of the Business Areas or Processes involved.

    Expected Content: Discussion of each business area or process that is expected to be included within the project with a set of Business Requirements that may act as the success criteria of the project.

    Purpose: To provide the client with a view of the potential project and the intended business benefits against estimated costs – such that the client can determine whether to proceed with the project.

    2. Functional Document

    The Functional Document should then take the areas identified by the earlier Scoping exercise and detail the Business Requirements in further detail alongside the Solution Requirements that are required by the solution to achieve these

    Based upon: Longer Workshop Schedule involving the Key Users and the Front-line staff involved with each of the Process Areas.

    Expected Content: Detailed focus on each business area or process with a description or diagram detailing the Processes involved and how the potential solution will affect or change these Processes, alongside a full Requirements Catalogue that may act as the success criteria of the solution.  Often we would expect the Functional Document to contain a section that details the expected User Experience for the end-users as a more practical vision of the intended Business Process.

    Purpose: To provide the client with full functional idea of how the solution will meet the aims laid out in the Scoping Document and how the proposed solution will operate for the end users.

    3. Solution Design Document

    The Solution Design Document then aims to take the Functional Document into the territory of explaining how the identified requirements will be implemented and the how the technologies (typically identified in the Scoping Phase or Functional Phase so that costs and other implications are identified well in advance) will mesh together to form the final solution.

    Based upon: The Solution Requirements and discussion with the Functional Consultant responsible for the earlier Functional Document.

    Expected Content: Specific Solution Design for how each of the requirements will be implemented, the skills required for the implementation that can then form a series of Work Packages and possible Development Phases.

    Purpose: To act as a focal point for both the client and implementation team to understand how the requirements outlined in the earlier Functional Document will be implemented and to give further detail on how the solution will operate for the end-users.

    4. Technical Design Documents

    The Technical Design Documents then detail individual technical areas of the project to communicate the design from architect to developer.

    Based upon: The Solution Design for each the Requirements and the knowledge of how these can be implemented using the technologies chosen.

    Expected Content: The envisaged intention for how the Development piece will be implemented and the expected end point for the completed development and intended Test Plan.

    Purpose: To communicate or document one of the technical work packages to the Development and Test Team.

    The overarching purpose here to define each of the Project Documentation to ensure that each is fit for purpose and not overlapping each other (unless internationally) to give documents that the intended audience struggles to interpret.  This set of thoughts largely comes from two recent examples of projects that I worked upon:

    – An existing project that we picked up from another Consultancy where the only project documentation consisted of a 400-page document that ranged from general information about the project in-between lengthy listings of every field, development code and screenshots; the result being that the senior management outside of the project had little idea of the project’s purpose and why they should invest any further money into the project.  This sense of confusion about the project had been communicated down and so the project was largely a failure as a result – hence our company being called to initiate a Phase 2 which was more a case of re-doing Phase 1.

    – A project bid where we did a series of initial scoping workshops to produce a scoping document that the client would use to present to their board as a steering document.  We (for all sorts of reasons, some of them ours and some of them forced about us) blurred the line between Scoping Document and Functional/Design Document to produce a 200-page behemoth of a document which blended high-level overview with low-level technical detail, we did not win the deal..

    This is a slightly dangerous article to post on a blog, as well as the aim of a ‘good document’ being very subjective from one person to the next, obviously all of these points apply to writing a post here as well – with the slight ‘get-out-jail’ card in that a blog is free and as such a blog’s audience and purpose can be less well-defined, however you still have a format for your writing, an idea of who you are writing for, and a purpose to each posting that you are looking to communicate.  I’ll have to let you decide whether this blog meets the idea of good document writing as laid out here!

    Posted in Analysis/Design Documents, Consultancy, Documentation | Tagged | 2 Comments

    Shared Picklists in CRM 2011

    Often when working on CRM Projects we will want to present the user with a drop-down list of options for a field that we would then want to see repeated at different points within the user’s business process – for example we may want a ‘Business Type’ drop down field on all the Sales Entities to describe the type of business intended for the Opportunity, Quote, Order or Invoice within the business, and obviously we would want this drop down field mapped across for any Quotes or Orders resulting from the Opportunity or in turn any Invoices resulting from the Order.

    When looking at this kind of requirement in MSCRM 3 or 4, our options were somewhat limited by the lack of an ability to create a shared set of drop-down options that could be re-used in the different screens throughout a solution.

    To resolve this problem in CRM 2011, one of the great new features introduced is the ability to create a shared Picklist that allows us to define a group of drop-down selections that can then be re-used throughout the solution involved – to suit this Picklists are now known as Option Sets and can either be created as a single Option Set field on an entity in a similar fashion to CRM 4, or alternatively created as a shared Option Set within the Option Sets component of the CRM 2011 Customisation area:

    Shared Option Sets in CRM

    Option Sets Component in the CRM 2011 Customisation Area

    We can add a new Shared Option Set to this list to define a set of Picklist Options that can then be re-used across different Option Set fields in our solution.

    Creating a new shared Option Set

    Adding a new shared Option Set into the CRM 2011 Solution

    This allows us to add a new Option Set field into CRM 2011 and specify that this field uses the Shared Option Set as the field’s possible set of options.

    Creating a New Option Set field in CRM 2011

    Adding a new Option Set field in CRM 2011 that is attached to our Shared Option Set

    Which allows us to amend the Picklist options separate from the actual field itself – like many aspects of CRM 2011 compared to CRM 4, this separates out the two concepts: the set of options themselves, and the fields which use these options.

    Option Set Dropdown on Opportunity

    Seeing the shared Option Set attached to a field on the Opportunity Entity

    Whilst relatively minor this is a great addition to MSCRM as various MSCRM Projects that I have worked upon have had the requirement for a Shared Picklist, that would allow common picklists to be re-used throughout the project. In CRM 3 and 4 this often led to either creating a new entity and lookup fields which could often seem overkill, or recreating the same set of Picklist values several times and having to ensure that the Data Values matched to allow for mappings between entities which itself could easily become a problem in having to manage and administer the data duplication involved in this approach.

    CRM 2011 should remove these problems and give us the option between a shared Option Set or using a Lookup Entity depending on which is best for the solution as opposed to being limited by the system itself.

    In addition CRM 2011 also provides a set of default Option Sets for standard uses across different CRM Solutions which should reduce the need for bespoke option sets in many instances, particularly Fiscal Years and Months:

    A Yes/No Boolean Provides a simple boolean list No
    Activity Type Provides a list of the defined Activity Types in the solution Customisable by adding a new Activity Type
    Category (Connection) Provides a list of the defined type types of connections defined within the solution Can be Customised
    Component State Provides a list of the possible component published or not published states No
    Component Type Provides a list of the possible component types (Field, Form, Dashboard etc) within CRM 2011 No
    Dependency Type Provides a list of the type of Dependency in CRM 2011 – when an Solution Internal or a Published Component No
    Fiscal Period Provides a list of possible Fiscal Periods that a Business tracks their Sales or Performance by, such as a particular Quarter, Month or other period. No
    Fiscal Year Provides a list of possible Years between 1970 to 2038. No
    Goal Type Provides a simple list of possible target types, i.e. Whole Number, Decimal or Money amount No
    Month of the Year Provides a simple list of months of the year No
    Object Type List of possible CRM 2011 Asynchronous object types – System Job or Workflow Session No
    Pricing Error Provides a list of possible pricing errors that can occur within CRM 2011 No
    Validation Status Provides a list of possible validation statuses No
    Validation Status Reasons Provides a simple list of possible validation status reasons No
    Posted in CRM 2011, Customisation, MSCRM | Tagged , | 4 Comments

    Plugins in CRM 2011

    When looking at Plugins in CRM 2011, it is important to break this post down into two separate sections – with a link to a follow-up third section linking to a separate post on this blog detailing how to incorporate LINQ into 2011 Plugins.

    Compiling the Plugin Registration Tool ready for use with CRM 2011
    Developing and Registering a new CRM 2011 Plugin
    Using LINQ in CRM 2011 Plugins

    Compiling the Plugin Registration Tool ready for use with CRM 2011

    As with previous versions of Dynamics CRM, CRM 2011 requires the next generation of development tools to start developing and registering Plugins – so we have a few pre-requisites to take care of first:

    (1) First of all you will need the .NET 4 Framework installed on your development environment, you can download this from this from here.

    (2) Second we will need Visual Studio 2010 on your development environment – as this is required to open and compile the Plugin Registration Tool code that is included in the CRM 2011 SDK.

    (3) Thirdly we will need the Microsoft.IdentityModel reference available to your development environment to successfully compile and run the Plugin Registration Tool – this can be done via downloading and installing the Windows Identity Foundation package from the following link.

    These three steps should setup your development environment to open, compile and run the Plugin Registration Tool Solution within the CRM 2011 SDK.

    Compiling the CRM 2011 Plugin Registration Tool

    Compiling the CRM 2011 Plugin Registration Tool in Visual Studio 2010

    Running the Plugin Registration Tool then brings up the familiar interface for adding a new Connection to a MSCRM Deployment – in my case for this post I am using a connection to a hosted instance of the CRM 2011 Beta, and so need to supply my Hosted URL and Microsoft Passport credentials to then connect to CRM.

    CRM 2011 Plugin Registration Tool

    Running the CRM 2011 Plugin Registration Tool and creating a Connection to either a Hosted or On-Premise deployment of CRM 2011 Beta

    I have seen a few problems around this where the ‘An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.’ error is encountered after specifying your CRM 2011 details – the following step can work to resolve this problem:

    Look into your User Profile directory (C:\Users\[your user account name] in Windows 7), browse to a folder titled ‘LiveDeviceID’ and delete the LiveDevice.xml file contained within this folder. This removes any cached credentials from your workstation which may be blocking the connection between the Plugin Registration Tool and the hosted CRM 2011 Service.

    Thanks to the contributors on the thread, really helped me out when hitting this error.

    Once connected to our deployment of CRM 2011 – we can look at the list of Registered Plugins that we have in this deployment. Here we have the option to Register new Assemblies, Steps, Images and Service End-Points into our deployment of CRM 2011. Assemblies, Steps and Images should be familiar territory to anyone involved with CRM 4, Service End-Points are a new addition for CRM 2011 and may well form the topic of a future post, however for the moment we will be looking at registering a new Assembly to insert a simple piece of Plugin logic into CRM.

    To do this our first step is to compile a Plugin Assembly for CRM 2011 – simplest way to do this initially is to again look into the CRM 2011 SDK and open one of the provided example solutions in Visual Studio 2010. The solution found at ‘.\sdk\samplecode\cs\plug-ins’ in the SDK is a good starting point:

    Example SDK Plugin Solution

    Example SDK Plugin Solution in Visual Studio 2010

    This solution should be ready to go and so should compile into a DLL that we can then use the Plugin Registration Tool to register into CRM 2011.

    Registering the Example Plugin DLL

    Registering the Example Plugin DLL into CRM 2011 via the Plugin Registration Tool

    Notice here we have a new option for deciding to upload the Assembly in a Sandbox mode or not – the CRM SDK contains further details on the difference between using the Sandbox or None setting here.

    As with CRM 4, this will register the assembly and number of Plugins for use in CRM 2011 but not register where or when to execute these Plugins – for this we need to register a Step off the Plugin. One of the Plugins in this example solution is a simple Account Autonumber which for the code to make sense would require registration against the Account Pre-Create Event to make sense:

    Registering a Step for this Plugin to operate off

    Registering a new Step within CRM 2011 to invoke the example Account Number Plugin provided in the CRM 2011 SDK

    The options here for registering a new Step are similar to CRM 4 with a few tweaks and differences that could form the topics of future posts.

    Once registered, the list of Plugins and Steps should then resemble the following:

    Plugin Registration Tool - after Registration of the Plugin and Step

    Plugin Registration Tool after registering the Assembly, Plugin and Step

    With the Plugin and Step registered, we should be able to browse into CRM 2011 and test that the Plugin Logic is now in effect.

    As this is a simple Autonumber Plugin on the Account entity – we can add a new Account in CRM and check that the Account Number field is now being populated automatically:

    Creating a new Account in CRM 2011

    Creating a new Account in CRM 2011 to check the registration of the Plugin Step

    Post Creation of the Account

    The result in CRM 2011 as a result of the Plugin being registered on the Create Step of the Account Entity

    And we can see the Plugin in affect in CRM 2011 as a result of compiling and running the Plugin Registration Tool – obviously there is a variety of new options available within the Tool for CRM 2011, but otherwise the process is similar to CRM 4. Our next step in this post is to look into how we can develop a custom Plugin for CRM 2011 that we can then register and see the affect of.

    Developing and Registering a new CRM 2011 Plugin

    Plugins for CRM 4 took a great idea in Callouts for CRM 3 and improved the concept immensely by allowing code to be uploaded into the Database via the Plugin Registration Tool, removing the need for saving DLL files to disk and editing XML Config files that drove Callouts. The ability to upload managed code to a remote hosted or on-premise installation of Dynamics CRM that would then fire on a specific entity event gave developers and solution architects a huge range of business logic and integration options.

    Which goes some way to explain ‘not broke, don’t fix’ approach to Plugins in CRM 2011. The approach to coding and registering a Plugin is very similar in terms of Target Messages (the XML update that is happening to a record) and Pre/Post Images (the XML details describing the record before the update and after the update.

    Obviously the ability to then amend other records through the CRM Webservice/API is different and involves a learning curve (particularly the LINQ approach, althrough we have seen this previously in latter releases of the CRM 4 SDK) but the underlying development patterns for Plugin Execution and Message Pipeline should be familiar to anyone used to developing Plugins against CRM 4.

    If we take an example piece of business logic that we may want to implement for a client, say a simple integer field counting the number of Opportunities that have been recorded for a Customer Account, the following steps give an idea of how we could achieve this in CRM 2011:

    (1) Create a new Visual Studio 2010 Class Library Project, on the assumption we are not using a Plugin Template Project.

    (2) Add the required references from the CRM 2011 SDK.

    Microsoft.CRM.SDK.Proxy
    Microsoft.XRM.SDK

    (3) Add a final required reference from the .NET Framework

    System.ServiceModel
    System.Runtime.Serialization

    (4) In much the same fashion as CRM 4, our Plugin Assembly must be signed with a Strong Key to be deployed into a CRM 2011 Deployment.

    Signing the Plugin Assembly with a Strong Key

    Signing the Plugin Assembly with a Strong Key in Visual Studio 2010

    (5) With these steps done we can add a new Class Library code file for our Plugin in a similar fashion to CRM 4, with a few notable differences:

    The class we develop in the Plugin inherits from IPlugin interface within the SDK

    public class MyPluginName : IPlugin
    

    This then allows us to implement a single Execute method which, as per CRM 4, is the central Main() function that the Plugin executes as part of the CRM Message Pipeline.

    public void Execute(IServiceProvider serviceProvider)
    

    This takes in the servceProvider object that contains the incoming details about CRM that come from the CRM Message Pipeline – from this we can derive the Plugin Execution Context that we are familiar with from CRM 4.

    IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
    

    From the Plugin Execution Context, we can read back the Target Message, PreMessage Images or PostMessage Images in the usual fashion as CRM 4.

    if (context.PostEntityImages.Contains("PostImage") &&
        context.PostEntityImages["PostImage"] is Entity)
    {
        postMessageImage = (Entity)context.PostEntityImages["PostImage"];
    }
    else
    {
        throw new Exception("No Post Image Entity in Plugin Context for Message");
    }
    
    if (context.PreEntityImages.Contains("PreImage") &&
        context.PreEntityImages["PreImage"] is Entity)
    {
        preMessageImage = (Entity)context.PreEntityImages["PreImage"];
    }
    else
    {
        throw new Exception("No Pre Image Entity in Plugin Context for Message");
    }
    
    if (context.InputParameters.Contains("Target") &&
      context.InputParameters["Target"] is Entity)
    {
        targetMessageImage = (Entity) context.InputParameters["Target"];
    }
    else
    {
        throw new Exception("No Target Message Entity in Plugin Context for Message");
    }
    

    As per CRM 4, we can also use the Plugin Execution Context to obtain a limited reference to the CRM Webservice such that we can make Retrieve, Create or Update requests to CRM – the code here is slightly different from CRM 4 but the concept is similar:

    IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
    
    IOrganizationService service = factory.CreateOrganizationService(context.UserId);
    

    Beyond these points, the process of developing a Plugin for CRM 2011 can then use the same structure as CRM 4 – the most notably change being the underlying changes to how Webservice Requests are communicated to the CRM Webservice, however these are documented or in the process of being documented in the CRM 2011 SDK and may well form the subject of a further post on CRM 2011. An obvious change in the code below is the concept of the EntityReference property instead of CRM 4’s Lookup, Owner or Customer property types.

    The following table is a brief breakdown of how this changes reading back Lookup properties from a Plugin Image:

    CRM 4 CRM 2011
    Microsoft.Crm.Sdk.Key opportunityid = (Microsoft.Crm.Sdk.Key) postImage.Properties["opportunityid"];
    
    Guid opportunityRecordId = opportunityid.Value;
    
    EntityReference opportunityid = (EntityReference) postImage.Attributes["opportunityid"];
    
    Guid opportunityRecordId = opportunityid.Id;
    
    Microsoft.Crm.Sdk.Customer customerid = (Microsoft.Crm.Sdk.Customer)postImage.Properties["customerid"];
    
    string customerRecordType = customerid.type;
    Guid customerRecordId = customerid.Value;
    
    EntityReference customerid = (EntityReference) postImage.Attributes["customerid"];
    
    string customerRecordType = customerid.Type;
    Guid customerRecordId = customerid.Id;
    

    (6) From these points we can develop our Plugin Logic to work from within CRM 2011 – the following code describes a very basic Plugin to count Opportunities that are linked to an Account.

    using System;
    using System.ServiceModel;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Query;
    
    public class OpportunityCountPlugin : IPlugin
    {
        const string OPPORTUNITYCOUNT_DATABASENAME = "new_opportunitycount";
    
        public void Execute(IServiceProvider serviceProvider)
        {
            try
            {
                // Obtain the execution context from the service provider.
                IPluginExecutionContext context =
                    (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
    
                Entity opportunityEntityImage;
    
                #region Get Opportunity Entity Image
                if (context.MessageName == "Create")
                {
                    // use Post Image for Create Messages
                    if (context.PostEntityImages.Contains("PostImage") &&
                        context.PostEntityImages["PostImage"] is Entity)
                    {
                        opportunityEntityImage = (Entity)context.PostEntityImages["PostImage"];
                    }
                    else
                    {
                        throw new Exception("No Post Image Entity in Plugin Context for Create Message");
                    }
                }
                else if (context.MessageName == "Delete")
                {
                    // use Pre Image for Delete Messages
                    if (context.PreEntityImages.Contains("PreImage") &&
                        context.PreEntityImages["PreImage"] is Entity)
                    {
                        opportunityEntityImage = (Entity)context.PreEntityImages["PreImage"];
                    }
                    else
                    {
                        throw new Exception("No Pre Image Entity in Plugin Context for Delete Message");
                    }
                }
                else
                {
                    // Plugin not valid for any other Message Types
                    throw new Exception("OpportunityCountPlugin Plugin is not valid for the '" + context.MessageName + "' message type");
                }
    
                if (opportunityEntityImage.LogicalName != "opportunity")
                {
                    // plugin is valid for this entity
                    throw new Exception("OpportunityCountPlugin Plugin is not valid for the '" + opportunityEntityImage.LogicalName + "' entity type");
                }
                #endregion
    
                #region Plugin Business Logic
    
                // interrogate Opportunity Post Image XML to determine the 'customerid' field
                if (opportunityEntityImage.Attributes.Contains("customerid"))
                {
                    EntityReference customerid = (EntityReference)opportunityEntityImage.Attributes["customerid"];
    
                    if ((customerid.LogicalName == "account") && (!( customerid.Id.Equals(Guid.Empty) )))
                    {
                        // Get a reference to the Organization service.
                        IOrganizationServiceFactory factory =
                            (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
    
                        // Local variable for reference to CRM Service - as Plugins should be developed as stateless
                        // due to CRM 2011 Caching Plugin object instances
                        IOrganizationService service = factory.CreateOrganizationService(context.UserId);
    
                        int opportunities = GetOpportunityCount(customerid.Id, service);
    
                        UpdateOpportunityCount(customerid.Id, opportunities, service);
                    }
                }
    
                #endregion
                    
            }
            catch (Exception ex)
            {
                // Handle general exception
                throw new InvalidPluginExecutionException("CRM2011.Example.OpportunityCountPlugin --> Execute '" + ex.Message + "'");
            }
        }
    
        #region Plugin CRM Methods
        /// <summary>
        /// Returns a simple count of the number of Opportunities associated to an Account
        /// </summary>
        /// <param name="accountId">The GUID Id of the Account involved</param>
        /// <param name="service">The CRM 2011 Organization Service reference to retrieve the data from</param>
        /// <returns>The current number of Opportunities associated directly with the Account</returns>
        public int GetOpportunityCount(Guid accountId, IOrganizationService service)
        {
            try
            {
                QueryByAttribute queryByAttribute = new QueryByAttribute("opportunity");
                queryByAttribute.ColumnSet = new ColumnSet(new string[] { "opportunityid" });
                queryByAttribute.Attributes.Add("customerid");
                queryByAttribute.Values.Add(accountId);
    
                EntityCollection retrieved = service.RetrieveMultiple(queryByAttribute);
    
                return retrieved.Entities.Count;
            }
            catch (FaultException<OrganizationServiceFault> exceptionServiceCall)
            {
                throw new Exception("GetOpportunityCount [OrganizationServiceFault '" + exceptionServiceCall.Message + "']");
            }
            catch (Exception ex)
            {
                throw new Exception("GetOpportunityCount [GeneralException '" + ex.Message + "']");
            }
        }
    
        /// <summary>
        /// Updates a custom Opportunity Count field on the specified Account to a new integer value
        /// </summary>
        /// <param name="accountId">The GUID Id of the Account involved</param>
        /// <param name="opportunityCount">The new integer value to update the Opportunity Count field to</param>
        /// <param name="service">The CRM 2011 Organization Service reference to update to</param>
        public void UpdateOpportunityCount(Guid accountId, int opportunityCount, IOrganizationService service)
        {
            try
            {
                Entity updateAccount = new Entity("account");
                updateAccount.Attributes.Add("accountid", accountId);
                updateAccount.Attributes.Add(OPPORTUNITYCOUNT_DATABASENAME, opportunityCount);
    
                service.Update(updateAccount);
            }
            catch (FaultException<OrganizationServiceFault> exceptionServiceCall)
            {
                throw new Exception("UpdateOpportunityCount [OrganizationServiceFault '" + exceptionServiceCall.Message + "']");
            }
            catch (Exception ex)
            {
                throw new Exception("UpdateOpportunityCount [GeneralException '" + ex.Message + "']");
            }
        }
        #endregion
    }
    

    (7) With the code developed and compiled we can then register the Plugin into our deployment of CRM 2011 (currently Beta) to insert the developed Business Logic. In the simple Opportunity Count example above, this would mean registering the Plugin on Create or Delete of the Opportunity entity with associated Post or Pre Image.

    Registering a Custom Plugin into CRM 2011

    Registering the Example Opportunity Count Plugin into CRM 2011

    (8) With this done we should be able to see the Plugin in action – this Plugin fires whenever we delete or create an Opportunity that is attached to an Account, so we can add several Opportunities to a particular Account as way of seeing the ‘new_opportunitycount’ field update accordingly:

    Opportunity Count Plugin in action

    List of three Opportunities associated to a particular Account in CRM 2011

    Opportunity Count Plugin in action

    Resulting field in CRM 2011 automatically counting the 3 Opportunities associated to the Account

    Obviously this Plugin in itself is slightly flawed as the logic does not account for Opportunities that have a Contact linked as the Customer, and realistically a Report or Chart would be better placed to show a running tally of Opportunities per Account. However hopefully this gives an initial example of how developing a Plugin for CRM 2011 may differ from Plugins in CRM 4.

    From experience of CRM 3 and 4 Projects Plugins and Callouts are often the most popular method for advanced customisation of Dynamics CRM as, whilst firmly in the Development camp as opposed to Functional Customisation, Plugins can be simply developed and deployed as re-usable components into any CRM deployment that meets the Plugin dependencies for fields and entities.

    For adding LINQ Queries into CRM 2011 Plugins, the following post acts as a follow-up and may be helpful: Using LINQ in CRM 2011 Plugins

    Posted in CRM 2011, Development | 21 Comments