Tuesday, March 1, 2011

Debugging Silverlight web-resources that connect to CRM Online.

Debugging Silverlight web-resources in CRM 2011 can be pretty tedious when you have to constantly re-upload your XAP file to the CRM server and then manually attach the debugger to the IE process. If you run the Silverlight application from your own development machine (using the ASP.NET Development server) you can quickly start debugging by hitting F5 in Visual Studio, but the app won't be able to make any requests to the CRM server because of Silverlight's cross-domain restrictions.

If you have an on-premise CRM server you can get around the cross-domain restrictions by putting a ClientAccessPolicy.xml file in the root of the CRM web application (See Markus Konrad's post on creating the policy file), but CRM online won't let you upload a ClientAccessPolicy, so you have to be a little more clever.  One method is to just create a proxy to pass all the requests to the CRM server.

Creating a proxy for CRM SOAP requests
A quick note: This works for the SOAP Organization service and the example at the end of this post only uses the SOAP service. I'm not sure if a similar proxy would work for the REST endpoint  (Given the REST endpoint's limitation I rarely use it anyway).

To get web-service requests to go from a Silverlight application w just need a few things:
1. Create an aspx page in the web project that's hosting the Silverlight app that will act as our proxy.
2. Program the proxy page to forward all posted requests to the CRM Online web-service endpoint.
3. Authenticate against CRM Online and attach the LiveId security token to each request.
4. Configure the Silverlight app to point to the proxy page.


Steps 1 and 2 are actually pretty straightforward once you pull out fiddler and figure out what the soap requests work like. Step 3 is the complicated bit but the sample code in the CRM SDK provides an example of authenticating against CRM Online (see sdk\samplecode\cs\wsdlbasedproxies\online). Once you've authenticated and have the security token you can just inject the token right into the header of the SOAP request.

I've built an example application which you can download here.  It's just a simple Silverlight application that makes create, retrieve, update, delete, and execute request to CRM Online using the proxy described above.





















To try it out just update the constants defined at the top of Proxy.aspx.cs:
//Update these constants to match your CRM Online account
const string host = "orgName.api.crm.dynamics.com";
const string userName = "erik.pool@example.com";
const string password = "Password";

Also note the GetOrganizationService method in the Silverlight app. If the current page is running on "localhost" it assumes you're debugging and points the org service url at the proxy.aspx page.  This way the same silverlight app works on my dev machine and when it's deployed to CRM Online.

if (href.Contains("localhost"))
{
   Uri baseUrl = new Uri(href, UriKind.Absolute);
   orgServiceUrl = new Uri(baseUrl, "SoapProxy/Proxy.aspx");
}
else
{
   orgServiceUrl = new Uri(location.GetProperty("protocol") + "//" + location.GetProperty("host") + "/xrmservices/2011/organization.svc/web");
}

Download the source code here.

-Erik

9 comments:

  1. This is very cool! Thanks for putting this together!

    ReplyDelete
  2. Hi,

    This is great, and saves me so much time deploying to crm online.

    I had an error 'server not found', and this was because I was in asia pacific region and had to change the URN to be crm5. For europe it is crm4.

    I added the following code to the proxy ExecuteProxyRequest function.

    private void ExecuteProxyRequest( XmlNode body )
    {
    string appliesTo = "urn:crm:dynamics.com";

    if (host.IndexOf("crm4.dynamics.com") != -1)
    appliesTo = "urn:crm4:dynamics.com";
    if (host.IndexOf("crm5.dynamics.com") != -1)
    appliesTo = "urn:crm5:dynamics.com";

    ReplyDelete
  3. This is really very cool and time saving.

    Can you please walk me through the generation of Reference.cs

    Thanks
    varun

    ReplyDelete
  4. I generated the reference.cs class using the method described in this post: http://erikpool.blogspot.com/2010/12/how-to-use-crm-2011-organization-soap.html, but that's a little out of date now, since the CRM SDK now has a full walkthrough for using the SOAP service in silverlight: http://msdn.microsoft.com/en-us/library/gg594452.aspx

    ReplyDelete
  5. I cannot make this work. I keep getting the exception message (trying the Create method) "The remote server returned an error: NotFound.".

    I have added the code that Johan suggested but it doesn't fix the problem.

    Any known issues that could be causing this?

    /Aidal

    ReplyDelete
  6. Is it now working any more i used in few months back but its not working now. Do we need to do any changes to make it work.

    Thanks
    Varun

    ReplyDelete
  7. I actually made it work (well semi work) because I could make it create an account but somewhere after execution but before callback, something goes wrong somehow.

    I could see with Fiddler, that the reply does contain what I was expecting, but the callback method has an exception in e.Error, indicating that the operation failed, even though it didn't.

    I dunno why this is, but I need to check for errors in my callback methods, so from my code the execution will look like it failed every time it seems.

    ReplyDelete
  8. Varun, it looks like Microsoft made a change (related to how CRM handles gzip compression) that breaks the code I posted before. The code can be made to work again by removing line 56 from Proxy.aspx.cs. Remove this line => req.Headers.Add("Accept-Encoding", "gzip, deflate");

    Aidal, I can't repro your scenario exactly but I think it's probably the same issue with the gzip compression.

    ReplyDelete
  9. Hi Erik,

    Is it possible to do this without the aspx page? I want to connect to the CRM 2011 Online soap service from an out of browser silverlight application...

    Thanks

    ReplyDelete