This project is read-only.
Description
Currently in the SDK in the \sdk\samplecode\cs\client\SoapLogger folder there is a great solution called the SOAP Logger that allows you to capture SOAP packets for CRM 2011 based on C# calls in .NET. This is great for debugging purposes but is also very helpful when trying to generate Jscript calls by the methodology shown here: http://mileyja.blogspot.com/2011/03/crm-2011-jscript-soap-request-formatter.html

Up until now, there was only a tool for C# and none available for VB.NET. I have taken the SDK C# version, referenced and built upon it, and created a version that works the same way for VB.NET. This allows you to write your .NET calls that you want to generate in Jscript or debug in VB.NET instead of C#. I feel that there hasn't been a whole lot of good support out there in the VB.NET realm for CRM developers and this should help remedy part of that problem..

More Information
What if you could take the XML from a captured request and create one of those things instantaniously? Well, now you can with my CRM 2011 Jscript SOAP Request Formatter, hosted on CodePlex, it takes much of the tediousness out of formatting straight XML for Jscript SOAP calls within CRM 2011.

Link to CodePlex project: http://crm2011soap.codeplex.com/

How Do You Use It.

It would be nice to just use fiddler for viewing traffic, but if you view the traffic on the new organization service endpoint you can't see any of the traffic as it is encrypted
In the SDK, if you go to /SDK/helpercode/cs/client, there is a soaplogger solution included that allows you to capture decrypted SOAP envelope requests and responses that you send to and from the CRM 2011 SOAP Endpoint.

There is a tutorial on creating interactive web resource libraries from the soaplogger captured requests here:

http://msdn.microsoft.com/en-us/library/gg594434.aspx

When you finish with the soaplogger you get an output text that contains a request envelope in the "HTTP REQUEST" section of the document that might look like this:



<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<request i:type="a:InsertOptionValueRequest" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
<a:Parameters xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
<a:KeyValuePairOfstringanyType>
<b:key>Label</b:key>
<b:value i:type="a:Label">
<a:LocalizedLabels>
<a:LocalizedLabel>
<a:IsManaged i:nil="true" />
<a:Label>testoptionCode9</a:Label>
<a:LanguageCode>1033</a:LanguageCode>
</a:LocalizedLabel>
</a:LocalizedLabels>
<a:UserLocalizedLabel i:nil="true" />
</b:value>
</a:KeyValuePairOfstringanyType>
<a:KeyValuePairOfstringanyType>
<b:key>AttributeLogicalName</b:key>
<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema%22%3Enew_testoptionset%3C/b:value>
</a:KeyValuePairOfstringanyType>
<a:KeyValuePairOfstringanyType>
<b:key>EntityLogicalName</b:key>
<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema%22%3Eaccount%3C/b:value>
</a:KeyValuePairOfstringanyType>
</a:Parameters>
<a:RequestId i:nil="true" />
<a:RequestName>InsertOptionValue</a:RequestName>
</request>
</Execute>
</s:Body>
</s:Envelope>

Ok, you can open the .exe file contained in the /bin/debug folder from the downloaded release now.



Next, if you copy this envelope into the Soap Formatter UI (right now you have to do all copy and past and select-all operations using right click on the textbox in the Soap Formatter UI :( I am sure this will be fixed at some later point) and type in a namespace (we will use "EXAMPLE") and a function name (we will use "InsertOptionValue"). It should look like this.






Now if you hit generate it will tranform the insert XML into the JScript call that is ready to be imported in CRM as a Web Resource and utilized on your forms. This is what it will look like using our example.



if (typeof (SDK) == "undefined")
{ SDK = { __namespace: true }; }
//This will establish a more unique namespace for functions in this library. This will reduce the
// potential for functions to be overwritten due to a duplicate name when the library is loaded.
SDK.EXAMPLE = {
_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;
},
InsertOptionValueRequest: function () {
var requestMain = ""
requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
requestMain += " <s:Body>";
requestMain += " <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
requestMain += " <request i:type=\"a:InsertOptionValueRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">";
requestMain += " <a:Parameters xmlns:b=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
requestMain += " <a:KeyValuePairOfstringanyType>";
requestMain += " <b:key>Label</b:key>";
requestMain += " <b:value i:type=\"a:Label\">";
requestMain += " <a:LocalizedLabels>";
requestMain += " <a:LocalizedLabel>";
requestMain += " <a:IsManaged i:nil=\"true\" />";
requestMain += " <a:Label>testoptionCode9</a:Label>";
requestMain += " <a:LanguageCode>1033</a:LanguageCode>";
requestMain += " </a:LocalizedLabel>";
requestMain += " </a:LocalizedLabels>";
requestMain += " <a:UserLocalizedLabel i:nil=\"true\" />";
requestMain += " </b:value>";
requestMain += " </a:KeyValuePairOfstringanyType>";
requestMain += " <a:KeyValuePairOfstringanyType>";
requestMain += " <b:key>AttributeLogicalName</b:key>";
requestMain += " <b:value i:type=\"c:string\" xmlns:c=\"http://www.w3.org/2001/XMLSchema\">new_testoptionset</b:value>";
requestMain += " </a:KeyValuePairOfstringanyType>";
requestMain += " <a:KeyValuePairOfstringanyType>";
requestMain += " <b:key>EntityLogicalName</b:key>";
requestMain += " <b:value i:type=\"c:string\" xmlns:c=\"http://www.w3.org/2001/XMLSchema\">account</b:value>";
requestMain += " </a:KeyValuePairOfstringanyType>";
requestMain += " </a:Parameters>";
requestMain += " <a:RequestId i:nil=\"true\" />";
requestMain += " <a:RequestName>InsertOptionValue</a:RequestName>";
requestMain += " </request>";
requestMain += " </Execute>";
requestMain += " </s:Body>";
requestMain += "</s:Envelope>";
var req = new XMLHttpRequest();
req.open("POST", SDK.EXAMPLE._getServerUrl(), true)
// Responses will return XML. It isn't possible to return JSON.
req.setRequestHeader("Accept", "application/xml, text/xml, */*");
req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
var successCallback = null;
var errorCallback = null;
req.onreadystatechange = function () { SDK.EXAMPLE.InsertOptionValueResponse(req, successCallback, errorCallback); };
req.send(requestMain);
},
InsertOptionValueResponse: function (req, successCallback, errorCallback) {
///<summary>
/// Recieves the assign response
///</summary>
///<param name="req" Type="XMLHttpRequest">
/// The XMLHttpRequest response
///</param>
///<param name="successCallback" Type="Function">
/// The function to perform when an successfult response is returned.
/// For this message no data is returned so a success callback is not really necessary.
///</param>
///<param name="errorCallback" Type="Function">
/// The function to perform when an error is returned.
/// This function accepts a JScript error returned by the _getError function
///</param>
if (req.readyState == 4) {
if (req.status == 200) {
if (successCallback != null)
{ successCallback(); }
}
else {
errorCallback(SDK.EXAMPLE._getError(req.responseXML));
}
}
},
_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);
},
namespace: true
};




You can now parse in your form fields or values as needed and you can call the function in the eventhandler setup by using the syntax SDK.{namespace}.{function name}Request, or in our case:

SDK.EXAMPLE.InsertOptionValueRequest

I hope this helps!

Last edited Oct 4, 2012 at 3:18 PM by Jamie_Miley, version 2