Friday, January 27, 2012

Config-less WCF Service Endpoint Binding for CRM 2011 On-Premise

I previously blogged about how to consume CRM 2011 WCF Services through adding service reference in Visual Studio. Sometimes, there might be a need that you want to initialize the service in your code without the config file.

So here is the method that does this job for CRM 2011 on-premise deployment.

private static CustomBinding GetServiceEndpointBinding(string bindingName)
{
     var endpointBinding = new CustomBinding
                               {
                                   Name = bindingName
                               };

     // Configure security binding
     var securityElement = SecurityBindingElement.CreateSspiNegotiationBindingElement(true);
     securityElement.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Default;
     securityElement.KeyEntropyMode = SecurityKeyEntropyMode.CombinedEntropy;
     securityElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
     securityElement.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncryptAndEncryptSignature;
     securityElement.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
     securityElement.IncludeTimestamp = true;

     var messageEncoding = new TextMessageEncodingBindingElement();
     messageEncoding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
     messageEncoding.ReaderQuotas.MaxArrayLength = int.MaxValue;
     messageEncoding.ReaderQuotas.MaxBytesPerRead = int.MaxValue;

     var transport = new HttpTransportBindingElement
                              {
                                   MaxBufferSize = int.MaxValue,
                                   MaxReceivedMessageSize = int.MaxValue
                              };

     // Add the SymmetricSecurityBindingElement to the BindingElementCollection.
     endpointBinding.Elements.Add(securityElement);
     endpointBinding.Elements.Add(messageEncoding);
     endpointBinding.Elements.Add(transport);

     return endpointBinding;
}

To use the above method, you can wire it up like this.
 var endpoint = new EndpointAddress("http://CrmServerName/CrmOrgName/XRMServices/2011/Organization.svc");

 var endpointBinding = GetServiceEndpointBinding("CustomBinding_IOrganizationService");

 var organizationService = new OrganizationServiceClient(endpointBinding, endpoint);

 // Provide login credential, you should only need one of the following
 
 // If you are using integrated authentication
 organizationService.ChannelFactory.Credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;

 // if you are using named CRM account
 // organizationService.ClientCredentials.Windows.ClientCredential = new NetworkCredential("UserName", "Password", "Domain");

This is a by-product of my SSIS Integration Toolkit development effort. As you may or may not appreciate, I took me quite some hours to get to the point that the code works for CRM on-premise deployment.

It should be noted that the above snippet works only for CRM on-premise deployment. CRM online or CRM federated deployments have vastly different WCF bindings from on-premise, which require quite some extra effort in order to be able to connect successfully.

Considering the majority of CRM installation is on-premise deployment, I hope this post helps some people in the community who have the same needs.

12 comments:

  1. Thanks for the great post, I tried your code, but keep getting the exception below?

    Security Support Provider Interface (SSPI) authentication failed. The server may not be running in an account with identity 'host/dotcrm'. If the server is running in a service account (Network Service for example), specify the account's ServicePrincipalName as the identity in the EndpointAddress for the server. If the server is running in a user account, specify the account's UserPrincipalName as the identity in the EndpointAddress for the server.

    ReplyDelete
  2. Hi rudgr, I am not sure about the cause of the exception. To verify my code, I have just tried another time, and it has worked for me. By any chance, you have configured IFD on your CRM server? The code snippet won't work for IFD, which I mentioned in the post. Not sure if that's the case.

    ReplyDelete
  3. @rudgr, I added a bit more code to the second snippet, so that you can specify login credentials for the organization service. Not sure if that's the cause of your problem.

    ReplyDelete
  4. Hi Daniel,

    Not sure if my problem is related to binding (although it seems so...)

    I have an Umbraco website, stored on Windows Azure, which connects to a CRM 2011 Online, for membership authentication. In the web.config of Umbraco, I just store the url of the CRM organization, username and password (also the device id). Everything seemed to work fine, but after a while, I got errors like:

    "MessageSecurityException
    Exception message: An unsecured or incorrectly secured fault was received from the other party.

    Server stack trace:
    at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.ProcessReply(Message reply, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
    at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
    at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
    at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
    at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

    If I restart the IIS on Windows Azure (by RDC), everything works ok for a while ...

    ReplyDelete
    Replies
    1. @Mihai, for CRM Onine, there is a ticket token involved, which expires every few hours.

      BTW, your problem doesn't seem to be related to this post. ;-)

      Delete
  5. Hi Daniel, thanks for the suggestion, but could you please exemplify ?

    I'm simply creating a new OrganizationServiceProxy(organizationUri, homeRealm, creds, devCreds) (with parameters read from the web.config), after that, the OrganizationService, OrganizationServiceContext, where should I add this ticket token ?

    Thank you.

    ReplyDelete
    Replies
    1. @Mihai, one simple solution to get around this issue is to not reuse the service proxy object, instead you instantiate a new one every time you make the service call. It adds some small overhead, but it should be OK. The instantiation process should probably include the device credential as well.

      Delete
    2. Hi Daniel,

      Thanks for the suggestion, I will do that.

      Delete
  6. Hi Daniel,

    i have developed one .net application which i was already hosted on windows azure.this application performs Create,Delete,Read,Update operation with CRM 2011 online.It is working perfect, but from the same application when i tried to connect to CRM 2011 on-premise.It throws an error like "Metadata contains a reference that cannot be resolved: ".please help me

    ReplyDelete
    Replies
    1. @shag, it is most likely that your CRM application is not accessible from external network. You can try to access your on-premise CRM from public network to see if it is actually accessible.

      Delete
    2. Hey Daniel,

      I have checked my CRM 2011 on-premise is working properly..even if i will put the CRM crud operation code in web role of azure,it works fine but when i tried the same in worker role.It throws the above error..

      Delete
    3. @shag, I haven't done the same so I am not sure what the exact problem is. I suspect that there is infrastructure limitation with Azure Worker role, which probably prohibits service calls to external network (from Azure point of view, your on-premise CRM is an external application to them). You can possibly ask in an Azure forum to see if this is a known issue.

      Again, I don't have an environment to verify this, so the information may not help. I would apologize if that's the case.

      Delete