In CRM 4.0, you can take advantage of the built-in RemoteCommand javascript function found in \_static\_controls\RemoteCommands\RemoteCommands.js. The RemoteCommand function executes a SOAP request to a webservice and parses the result into an easily managable object. You can easily pass parameters, and evaluate the response from the webserver method.
Building a Basic Webservice
Here are the steps to build and test a basic webservice in CRM 4.
- Build a webservice
- Deploy the webservice to CRM
- Modify a copy of CRM’s RemoteCommand.js file
- Add javascript to a form’s event to call the webservice and process the response
- Test and debug the webservice
The following sections cover these three steps in detail
Build a Webservice
- In VS2008, choose
File>New>Project and choose ASP.NET Web Service Application
- Target the appropriate .NET Framework for the deployment. See the dropdown in the upper-right of the New Project window
- Name the webservice appropriately and Click OK
- Delete Service1 from the project
- Right-click the Project and choose
Add>New Item. Choose Web Service, name it appropriately and click Add. The name can be the same as your project name
- You must modify the
Namespace:=”http://tempuri.org/” tag to Namespace:=” http://schemas.microsoft.com/crm/2006/WebServices” or your webservice call will fail. This is the namespace that RemoteCommand uses.
- You’ll see that a HelloWorld WebMethod is created in your class. You can test the CRM javascript webservice call with this webmethod
Deploy the Webservice to CRM
- Build the webservice project in debug mode
- Create the following folder structure:
<CRM Web Root>\ISV\Allin\WebServices.
- Copy the
.asmx file from your project to the folder you just created.
- Copy the
.dll in the bin folder of your project to the <CRM Web Root>\bin folder.
Modify a Copy of CRM’s RemoteCommand.js file
You can call a custom webservice in a CRM page’s javascript using CRM’s RemoteCommand.js. This file contains a function called RemoteCommand that will construct a SOAP request with parameters you specify and calls any webservice you specify. The catch is that it is an unsupported feature in CRM. If Microsoft decides to make changes to, or replace RemoteCommand.js in future releases of CRM, then your use of RemoteCommand.js may not be supported. To circumvent possible incompatibility problems with future releases, we can copy and modify CRM’s RemoteCommand.js file and use our copy of AllinRemoteCommand.js. Here’s what to do
- Copy the
<CRM Root> \_static\_controls\RemoteCommands\RemoteCommand.js to <CRM Web Root>\ISV\Allin\Javascript\AllinRemoteCommand.js
Open the AllinRemoteCommand.js file with a text editor and make the following text replacements throught the file:
- RemoteCommand → AllinRemoteCommand
- RemoteCommandDefaultErrorHandler → AllinRemoteCommandDefaultErrorHandler
- AsyncResultHandler → AllinAsyncResultHandler
- RemoteCommandResult → AllinRemoteCommandResult
- CommandParameter → AllinCommandParameter
Add Javascript
- Pick a form and add the following code to the OnLoad event. This code dynamically includes the
AllinRemoteCommand.js file in the page: var util = document.createElement('script');
util.type = 'text/javascript';util.src = '/ISV/Allin/Javascript/AllinRemoteCommand.js';
document.getElementsByTagName('head')[0].appendChild(util);
- Add the following code to an attribute’s OnChange event. This will cause the webservice to be called when a user changes the value of that attribute. A picklist of lookup is the best type of attribute to use:
var cmd = new AllinRemoteCommand("<YourWebServiceName>", "HelloWorld","/<CRMSERVER>/ISV/Allin/WebServices/");
var result = cmd.Execute();
alert(result.ReturnValue);
- Save and Publish the change.
Test and Debug
In CRM open the Form that you modified. Make a change to the value of the attribute that you modified and see if the MessageBox appears with the message “Hello World”. If so, your page successfully called your webservice from javascript! If not, debug by viewing request and response with Fiddler (download from fiddlertool.com).
Modify Webservice to Pass Parameters
- Modify your project’s HelloWorld function like so:
Public Function HelloWorld(ByVal name as String, ByVal age as Integer) As String
Return name & “ is “ & age.ToString
End Function
- Build and copy files following the steps in Deploy the Webservice to CRM.
- Modify the CRM form’s
OnLoad event with cmd.SetParameter like so: var cmd = new AllinRemoteCommand("/ISV/Allin/WebServices/");
cmd.SetParameter("name", "Craig");
cmd.SetParameter("age", 42);
var result = cmd.Execute();
alert(result.ReturnValue);
- Save and Publish the change. Test by going to the form in CRM and seeing the MessageBox display “Craig is 42”.
Incorporate CRM SDK in Webservice
- Add references to the project for microsoft.crm.sdk.dll and microsoft.crm.sdktypeproxy.dll.
- Add Imports statements to the .asmx page for Microsoft.Crm.Sdk and Microsoft.Crm.SdkTypeProxy.
- Use CrmService and associated classes to query the CRM database.
GetOppLeadSource Webservice
The following code is a complete webservice with a WebMethod that takes an id as a string and returns a object that contains Status, OppleadSource, OppLeadSourceOther and ErrorMessage. Note: where no value is returned, the property is set to Nothing. This has the effect of leaving out the node for that property in the XML response. Your javascript needs to handle the condition when no property is returned. The next section shows the javascript that calls this webmethod and handles the response.
AllinWebService.asmx.vb:
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.ComponentModel
Imports Microsoft.Crm.Sdk
Imports Microsoft.Crm.SdkTypeProxy
<System.Web.Services.WebService(Namespace:="http://schemas.microsoft.com/crm/2006/WebServices")> _
<System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<ToolboxItem(False)> _
Public Class AllinWebService
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function HelloWorld(ByVal name As String, ByVal age As Integer) As String
Return name & " is " & age.ToString
End Function
Public Class ReturnValue
Public Status As String
Public OppLeadSource As String
Public OppLeadSourceOther As String
Public SourceCampaignId As String
Public SourceCampaignName As String
Public ErrorMessage As String
End Class
<WebMethod()> _
Public Function GetOppLeadSource(ByVal id As String) As ReturnValue
Dim result As New ReturnValue
Try
Dim colSet As New Query.ColumnSet(New String() {"ht_oppleadsource", "ht_oppleadsourceother"})
Dim targetRetrieve As New TargetRetrieveDynamic()
targetRetrieve.EntityName = EntityName.opportunity.ToString()
targetRetrieve.EntityId = New Guid(id)
Dim retrieve As New RetrieveRequest()
retrieve.Target = targetRetrieve
retrieve.ColumnSet = colSet
retrieve.ReturnDynamicEntities = True
Dim service As CrmService = GetMyService()
Dim retrieved As RetrieveResponse = CType(service.Execute(retrieve), RetrieveResponse)
Dim dynEntity As DynamicEntity = CType(retrieved.BusinessEntity, DynamicEntity)
result.Status = "success"
result.OppLeadSource = GetTextValue(dynEntity, "ht_oppleadsource")
result.OppLeadSourceOther = GetTextValue(dynEntity, "ht_oppleadsourceother")
result.SourceCampaignId = GetTextValue(dynEntity, "ht_campaignid")
If result.SourceCampaignId IsNot Nothing Then
Dim campaignRetrieve As New TargetRetrieveDynamic
campaignRetrieve.EntityName = EntityName.campaign.ToString()
campaignRetrieve.EntityId = New Guid(result.SourceCampaignId)
retrieve = New RetrieveRequest()
retrieve.Target = campaignRetrieve
retrieve.ColumnSet = New Query.ColumnSet(New String() {"name"}) 'New Query.AllColumns '
retrieve.ReturnDynamicEntities = True
retrieved = CType(service.Execute(retrieve), RetrieveResponse)
dynEntity = CType(retrieved.BusinessEntity, DynamicEntity)
result.SourceCampaignName = GetTextValue(dynEntity, "name")
End If
Catch ex As Exception
result.Status = "failure"
result.OppLeadSource = Nothing
result.OppLeadSourceOther = Nothing
result.ErrorMessage = String.Format("guid = {0}, error = {1}", id, ex.Message)
End Try
Return result
End Function
Private Function GetTextValue(ByRef dynEntity As DynamicEntity, ByRef attribute As String) As String
If dynEntity.Properties.Contains(attribute) Then
If (dynEntity(attribute).GetType Is GetType(Picklist)) Then
Return CType(dynEntity(attribute), Picklist).Value.ToString
Else
Return dynEntity(attribute).ToString
End If
Else
Return Nothing
End If
End Function
Private Function GetMyService() As CrmService
Dim service As New CrmService()
service.Credentials = System.Net.CredentialCache.DefaultCredentials
service.Url = "http://gfscrmserver/MSCrmServices/2007/CrmService.asmx"
Dim token As CrmAuthenticationToken = New CrmAuthenticationToken()
token.OrganizationName = "GFSCRM"
token.AuthenticationType = 0
service.CrmAuthenticationTokenValue = token
Return service
End Function
End Class
Javascript for Calling and Handling Response from GetOppLeadSource
Place this code in the Account form’s Opportunity field’s OnChange Event:
var lookupData = new Array;
lookupData = crmForm.all.gfs_opportunityid.DataValue;
if ((lookupData != null) && (lookupData[0] != null))
{
var cmd = new AllinRemoteCommand
cmd.SetParameter("orgName", ORG_UNIQUE_NAME);
cmd.SetParameter("serverUrl", SERVER_URL);
cmd.SetParameter("id", lookupData[0].id);
var result = cmd.Execute();
if (!IsNull(result.ReturnValue.Status) && result.ReturnValue.Status == "success")
{
if (!IsNull(result.ReturnValue.OppLeadSource))
{
crmForm.all.htc_leadsource.DataValue = result.ReturnValue.OppLeadSource
}
else
{
crmForm.all.htc_leadsource.DataValue = null;
}
if (!IsNull(result.ReturnValue.OppLeadSourceOther))
{
crmForm.all.htc_leadsourceother.DataValue = result.ReturnValue.OppLeadSourceOther
}
else
{
crmForm.all.htc_leadsourceother.DataValue = null;
}
var lookupItem = new Object();
if (!IsNull(result.ReturnValue.SourceCampaignId) &&!IsNull(result.ReturnValue.SourceCampaignName))
{
lookupItem.typename = "campaign";
lookupItem.id = result.ReturnValue.SourceCampaignId;
lookupItem.name = result.ReturnValue.SourceCampaignName;
lookupData = new Array();
lookupData[0] = lookupItem;
crmForm.all.htc_campaignid.DataValue = lookupData;
}
else
{
crmForm.all.htc_campaignid.DataValue = null;
}
}
else
{
if (!IsNull(result.ReturnValue.ErrorMessage))
{
alert("A webservice error has occurred: " + result.ReturnValue.ErrorMessage)
}
else
{
alert("A webservice error has occurred")
}
}
}
else
{
crmForm.all.htc_leadsource.DataValue = null;
crmForm.all.htc_leadsourceother.DataValue = null;
crmForm.all.htc_campaignid.DataValue = null;
}
}
Code Notes
Notice that the return value for the GetOppLeadSource is an instance of a class that contains many public properties, each of type string. The AllinRemoteCommand handles receiving this data quite easily. Notice in the javascript code the data is accessed through a variable named result. Result has a property named ReturnValue, and ReturnValue has properties by the same name as the properties in your webmethod’s return value class. If you do not want to pass a value back for a property, set that property to Nothing in the webmethod. In the javascript, test for !IsNull( result.ReturnValue.<property>).