Category Archives: Dynamics

Accessing Microsoft Dynamics CRM 4 Web Services from Windows 8 Modern UI App

Visual Studio 2012 is a big improvement overall but the ability to add a web reference has now disappeared completely, even from the “Advanced” button in the add service reference feature as per VS2010. Although most documentation seems to suggest adding an old asmx service as a service reference should work fine, it doesn’t always. The solution below works for the Microsoft Dynamics v4 web services but it is probably also valid for consuming other old style asmx services in Modern UI apps.

The 2 main problems I encountered were

  • I couldn’t add a custom header. In this case the CrmAuthenticationToken which defines which authentication protocol to use. Without this I’d simply get “The HTTP request is unauthorized with client authentication scheme ‘Negotiate’. The authentication header received from the server was ‘Negotiate,NTLM’.”
  • When you import the service reference, the autogenerated code in References.cd create the elements as XmlElementAttribute objects with an order by e.g. XmlElementAttribute(Order=1) which cause elements to mysteriously get set to null even though Fiddler proves that they are definitely not null. The reason is that the code doesn’t read them if they appear out of order in the response XML which in the Dynamics 4 API, they often are.

The solution to the first problem was mostly provided in this blog post, with just a few tweaks needed to make it work with the Windows Store App framework. It essentially creates a new EndPoint behavior which manually adds the CrmAuthenticationToken into the header. Here is the class in it’s entirety.

using MyCompany.Windows8.App.PersonHelper.cRM;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace MyCompany.Windows8.App.PersonHelper.ServiceCall
{

    public class CrmServiceConnectionParams
    {
        public String Scheme { get; set; }

        public String Url { get; set; }

        public CrmAuthenticationToken AuthenticationToken { get; set; }

        public BasicHttpSecurityMode SecurityMode { get; set; }

        private void setSecurityModeFromScheme()
        {
            switch (Scheme)
            {
                case "https":
                    SecurityMode = BasicHttpSecurityMode.Transport;
                    break;
                default:
                    SecurityMode = BasicHttpSecurityMode.None;
                    break;
            }
        }

        public CrmServiceConnectionParams(String url, CrmAuthenticationToken token)
        {
            if (url.Contains("://"))
            {
                string[] urlSplit = url.Split(new string[] { "://" }, StringSplitOptions.None);

                Scheme = urlSplit[0];
                Url = urlSplit[1];
            }
            else
                throw new ArgumentException("Failure creating CrmServiceConnectionParams instance. Invalid or missing URL scheme (e.g. 'http://').");

            AuthenticationToken = token;

            setSecurityModeFromScheme();
        }

        public CrmServiceConnectionParams(String scheme, String url, CrmAuthenticationToken token)
        {
            Scheme = scheme;
            Url = url;
            AuthenticationToken = token;

            setSecurityModeFromScheme();
        }

        public CrmServiceConnectionParams(String scheme, String url, CrmAuthenticationToken token, BasicHttpSecurityMode securityMode)
        {
            Scheme = scheme;
            Url = url;
            AuthenticationToken = token;
            SecurityMode = securityMode;
        }
    }

    public class CrmServiceInstance
    {
        private CrmServiceConnectionParams connectionParams;
        public CrmServiceConnectionParams ConnectionParams
        {
            get { return connectionParams; }
            set
            {
                connectionParams = value;
                spawnCrmService();
            }
        }

        private CrmServiceSoapClient crmService;
        public CrmServiceSoapClient CrmService
        {
            get { return crmService; }
            set
            {
                crmService = value;
                isCrmServiceReady = true;

                if (OnCrmServiceReady != null)
                    OnCrmServiceReady(this, new EventArgs());
            }
        }

        #region OnCrmServiceReady Event

        public event EventHandler<EventArgs> OnCrmServiceReady;

        private bool isCrmServiceReady;
        public bool IsCrmServiceReady
        {
            get { return isCrmServiceReady; }
        }

        #endregion

        public CrmServiceSoapClient CreateCrmService(String crmServiceUrl, CrmAuthenticationToken authToken, BasicHttpSecurityMode securityMode)
        {
            BasicHttpBinding httpBinding = new BasicHttpBinding(securityMode);
            httpBinding.MaxReceivedMessageSize = Int32.MaxValue;
            httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;

            EndpointAddress crmEndpoint = new EndpointAddress(crmServiceUrl);

            CrmServiceSoapClient crmService = new CrmServiceSoapClient(httpBinding, crmEndpoint);

            MessageHeader authTokenHeader = MessageHeader.CreateHeader("CrmAuthenticationToken",
            "http://schemas.microsoft.com/crm/2007/WebServices", string.Empty, new CrmAuthenticationTokenSerializer(authToken));

            crmService.ChannelFactory.Endpoint.EndpointBehaviors.Add(new CrmServiceBehavior(new CrmServiceMessageInspector(authTokenHeader)));
            crmService.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
            crmService.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;

            return crmService;
        }

        private void spawnCrmService()
        {
            CrmService = CreateCrmService(
            ConnectionParams.Scheme + "://" + ConnectionParams.Url,
            ConnectionParams.AuthenticationToken,
            ConnectionParams.SecurityMode);
        }

        public CrmServiceInstance()
        {
            isCrmServiceReady = false;
        }

        private class CrmServiceMessageInspector : IClientMessageInspector
        {
            public MessageHeader ServiceHeader;

            #region IClientMessageInspector Members

            public void AfterReceiveReply(ref Message reply, object correlationState) { }

            public object BeforeSendRequest(ref Message request, IClientChannel channel)
            {
                request.Headers.Add(ServiceHeader);
                return null;
            }

            #endregion

            public CrmServiceMessageInspector(MessageHeader header)
            {
                ServiceHeader = header;
            }
        }

        private class CrmServiceBehavior : IEndpointBehavior
        {
            public CrmServiceMessageInspector ServiceInspector;

            #region IEndpointBehavior Members

            public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }

            public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
                clientRuntime.ClientMessageInspectors.Add(ServiceInspector);
            }

            public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher dispatcher)
            {
                throw new NotImplementedException(); // Silverlight does not invoke this method.
            }

            public void Validate(ServiceEndpoint endpoint) { }

            #endregion

            public CrmServiceBehavior(CrmServiceMessageInspector inspector)
            {
                ServiceInspector = inspector;
            }
        }
    }

    public class CrmAuthenticationTokenSerializer : XmlObjectSerializer
    {
        #region CrmAuthenticationTokenSerializer Members

        private readonly string authType;
        private readonly string organizationName;
        private readonly string callerId;
        private readonly string crmTicket;

        public CrmAuthenticationTokenSerializer(CrmAuthenticationToken authToken)
        {
            callerId = Guid.Empty.ToString();
            authType = authToken.AuthenticationType.ToString();
            organizationName = authToken.OrganizationName;
            crmTicket = authToken.CrmTicket;
        }

        #endregion

        #region XmlObjectSerializer Members

        public override bool IsStartObject(XmlDictionaryReader reader)
        {
            return true;
        }

        public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
        {
            return null;
        }

        public override void WriteEndObject(XmlDictionaryWriter writer)
        {
            return;
        }

        public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
        {
            string tokenXmlLiteral = String.Empty;

            tokenXmlLiteral += "<AuthenticationType xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>"
            + authType
            + "</AuthenticationType>";

            if (crmTicket != null && crmTicket != String.Empty)
                tokenXmlLiteral += "<CrmTicket xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>"
                + crmTicket
                + "</CrmTicket>";

            tokenXmlLiteral += "<OrganizationName xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>"
            + organizationName
            + "</OrganizationName>"
            + "<CallerId xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>"
            + callerId
            + "</CallerId>";

            writer.WriteRaw(tokenXmlLiteral);
        }

        public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
        {
            return;
        }

        #endregion
    }
}

So from the code that you want to make the call, you just create the client by using

CrmAuthenticationToken authToken = new CrmAuthenticationToken();
authToken.AuthenticationType = 0;
authToken.OrganizationName = "MyCompany";
authToken.CallerId = new Guid("00000000-0000-0000-0000-000000000000");

CrmServiceInstance instance = new CrmServiceInstance();
CrmServiceSoapClient client = instance.CreateCrmService("http://mycrmservername/MSCrmServices/2007/CrmService.asmx", authToken, BasicHttpSecurityMode.TransportCredentialOnly);

The solution to the 2nd problem was resolved in this blog post. Here are the steps I made to get it working.

  1. Click on PROJECT -> Show all files
    Show all files
  2. In the navigation pane browse to your service reference and open References.csOpen references file
  3. Open up Quick Find/Replace and search for “(\s*Order=\d+\s*\,?\s*)|(\,?\s*Order=\d+\s*)”, replace with nothing (i.e. blank) and make sure the replace all button is pressed. Then click Replace all. Note that if you ever re-import the service reference you will need to repeat thisFindReplace

Search with criteria for value in different XML node in an XSLT Template

There have been a few instances where I have had to filter the incoming XML to look for a value from a different node in the XML. For the example in this article I had the parent node id but I wanted to know the name of that parent node.

This excellent article gave me a starting point for the principle of searching for values in different nodes using an XSLT template. This worked spendedly in one map I was working where I was trying to sum up all values in the current list of node items.


<xsl:template name="OutputSum">
  <xsl:param name="param1" />
  <xsl:element name="TransactionAmount">
    <xsl:variable name="var:vTranAmt"
    select="sum(//ExpenseItem[ReportEntryID=$param1]/TransactionAdjustmentAmount" />

    <xsl:value-of select="$var:vTranAmt" />

  </xsl:element>
</xsl:template>

However, when I tried to use the same principle to select the name value of a parent node for which I only knew the ID, it just wouldn’t work. I kept gettiing blank values no matter what I tried.

The source schema was from the MS Dynamics CRM v4 web services using the RetrieveMultiple method and is much more complex than one I normally use. After a lot of headscratching I noticed that it was the namespaces that were causing the problems. When I right clicked the map, selected validate and viewed the .xsl that Visual Studio was generating I noticed in my other mappings that BizTalk was adding some bizarre namespaces to the nodes, e.g. s6:RetrieveMultipleResult/s4:BusinessEntities . Eventually I got it to work using these namespaces.


<xsl:template name="ParentCompanyCode">
  <xsl:param name="param1" />
  <xsl:element name="Code">

                <xsl:value-of select="//s4:BusinessEntity[s6:new_geographyid=$param1]/s6:new_name" />

  </xsl:element>
</xsl:template>

Unfortunately I can’t explain exactly why this was required, but my advise is that if you can’t work out why custom xsl isn’t working then create something similar using functoids and have a look through the xsl that it generates via the Validate map functionality.