Accessing Cross-Domain ADO.NET Data Services from the Silverlight Client Library
When we built the client library for Silverlight in Silverlight 2.0 , the underlying Network Stack provided by Silverlight core libraries
was very limited in terms of the Status Codes and Request Verbs that it supported.Our options were :
a) Use the System.Net libraries provided by the agClr and give a limited subset of the functionality available on the desktop.
b) Cook up our own Network stack and provide all the functionality provided on the desktop , in Silverlight.
We went with Option (b) and built a network stack that uses the browser’s underlying XmlHttpRequest object.
This means that the cross domain access available in Silverlight networking stack is not available in our networking stack.
Forums user Ben Hayat asked a question about the guidelines for Cross-Domain access using our Silverlight Client Library .
Now , this is not supported out of the box . But ,you can use the same solution as you would for an Ajax application ,
which is building a server-side proxy that talks to the remote service and is completely transparent to the client library.
Below is a sample Server-Side proxy that would help one talk to Data Services hosted on a different domain than the Silverlight application.
There are a couple of issues that a Server-Side proxy needs to solve for an ADO.NET Data Service ,
a) Appear totally transparent
b) Resolve Identities of resources to appear as they would from the Proxy service and not the original Data Service.
Issue a) is easy , Issue b) is a little more involved.
When the client library receives a Payload back as part of a query ,we construct the entity’s identity based on the following rules,
ex: If you browse to https://ServiceEndpoint /Northwind.svc/Customers('ALFKI') , the response looks like this :
<entry
xml:base="https://ServiceEndpointNorthwind.svc/" xmlns:d="https://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="https://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns="https://www.w3.org/2005/Atom">
<link rel="edit" title="Customers" href="Customers('ALFKI')" /> <id>https://ServiceEndpointNorthwind.svc/Customers('ALFKI')</id>
<content type="application/xml">
<m:properties>
Properties go here
</m:properties>
</content>
</entry>
When we get the above payload back as a response , we create a Customer object and its identity is the value of the <id> field in the payload.
When you make any edits to this Customer object, the edits are sent to the URI specified in the <link rel=”edit”> value of the payload.
Which means that the proxy server has to modify the payload it recieves from the real Data Service to make sure that all links , Id and Edit
point to the proxy Data Service and not the real Data Service. The easiest way to do this is to do a String.Replace replacing all occurrences of the
the original Service end point with the Proxy Service end point in the payload.
Without further stalling , here is the Proxy Handler.
A couple of words of caution so that I can keep my job,
This is only a sample implementation of what a proxy could look like.
Do NOT use this in your production applications.
This is NOT an official solution provided by the ADO.NET Data Services team ,
this is just me illustrating how to write a proxy.
If you download this sample , it means that you understand these rules .
For any problems with this sample , leave a comment on my blog ,
the ADO.NET Data Services Forums is NOT the right place for any issues with this proxy.
Running the sample
Download the sources from the link above.
Open the solution in Visual Studio 2008 and hit F5.
You should see the Silverlight application come up and
get the data from the Data Service hosted in a different application.
The Cross Domain Proxy is a generic AShx handler which receives the requests from the Client library and forwards them onto the
original Data Service , gets the response , changes the ID and Edit Links and returns the response to the Silverlight Client .
Client Sends request to Proxy Handler
Proxy Handler sends the request data to the Original Data Service
Data Service responds with response
Proxy handler reads response and changes the ID and EDIT links in the payload
In case of POST , the Proxy Handler changes the response “Location” Header
so that the identity points to the Proxy Handler and not the Data Service.
Proxy handler returns the response data to the client .
How do I use this in my application ?
The source available for download contains “CrossDomainHandlerBase” which is the class that contains the functionality required
for recieving requests from clients and forwarding the requests onto the Data Service.
Setting up the Server-Side Proxy
- Add a Generic ASHX Handler to the application that contains the Web page hosting the Silverlight application ,
2. This will create a Generic Handler which implements IHttpHandler.
3. Remove the code stubs for IHttpHandler methods and properties .
4. Replace IHttpHandler with “CrossDomainHandlerBase”.
[WebService(Namespace = "https://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class CrossDomainHandler : CrossDomainHandlerBase
{
public CrossDomainHandler()
: base()
{
this.DataServiceURI = "URI FOR THE DATA SERVICE";
this.ProxyServiceURI = "URI FOR THE PROXY SERVICE";
}
}
4.From this point on , for all purposes , the Proxy Handler service IS the Data Service
Setting up the Silverlight application for Cross-Domain access
Right-Click on “References” in the Project
Select "Add Service Reference"
In the “Address” field , enter the URI for the proxy service ,
In case of the sample solution provided, the Provider is called “DataProvider”, hence
the code-gen produces the code with a type “DataProvider” derived from DataServiceContext.
When you instantiate the Context in the Silverlight application , always specify the Base URI
as the Proxy Handler’s URI,
DataProvider dataProvider = new DataProvider( new Uri("RELATIVE URI FOR THE PROXY SERVICE", UriKind.RelativeOrAbsolute) );
This is the most important step of all , Party on !!
To do in the near future :
- Convert the Handler to be an IHttpAsyncHandler so that we dont block on waiting for the response from the Data Service
- Provide better mechanisms for Identity Resolution other than String.Replace .
- Write a better demo app than something which binds all the parts of my name to a Grid.
- Your suggestion goes here .
Comments
Anonymous
June 11, 2009
PingBack from http://castironbakeware.info/story.php?title=with-great-power-comes-great-response-write-ability-accessingAnonymous
September 09, 2009
Hi, Thanks for posting the workaround. I am able to use the workaround to get the data. But, I am receiving error when i insert data. Error is 'The expected content type for a batch requests is "multipart/mixed;boundary=batch" not "application/atom+xml; charset=utf-8'. I am using following call to insert object context.BeginSaveChanges(SaveChangesOptions.Batch , new AsyncCallback(Save_Complete), null); In my Save_Complete method, it throws error at following point DataServiceResponse response = (DataServiceResponse)context.EndSaveChanges(result); Any idea how to make it work?Anonymous
September 09, 2009
Hi Govind, With the release of CTP2 , this workaround is no longer needed. Please refer to this blog post : http://blogs.msdn.com/astoriateam/archive/2009/09/03/using-the-ado-net-data-services-silverlight-client-in-x-domain-and-out-of-browser-scenarios-i.aspx PhaniRaj