Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

WCF Integration in Silverlight 2 Beta 1

Pete Brown - 19 March 2008

In preparation for my Silverlight for Business Applications webcast today, I decided to look more deeply into how Silverlight works with services. I originally concentrated a lot on security, and had a much larger post on how to secure your services, but ran into some issues there. More on that below.

WCF Service Requirements for Silverlight

In Silverlight 2 Beta 1, WCF services must be set up to use basicHttpBinding. Basic HTTP Binding sets up a SOAP 1.1 endpoint in your service. To enable that, before setting the service reference in your Silverlight application, change the service's web.config from the default wsHttpBinding to basicHttpBinding:

<system.serviceModel>
...
    <services>
        <service behaviorConfiguration="ServiceBehavior" name="Service">
            <endpoint address="" 
              binding="basicHttpBinding" 
              contract="IService">
                <identity>
                    <dns value="localhost"/>
                </identity>
            </endpoint>
            <endpoint address="mex" 
              binding="mexHttpBinding" contract="IMetadataExchange"/>
        </service>
    </services>
</system.serviceModel>

 

If you forget to do that before adding a service reference, no big deal. Just refresh your service reference in your Silverlight project. If that doesn't work, remove and re-add the reference.

Enabling Exception Information in WCF

During debugging, you may want to get exception information back from WCF. In order to enable that, change the includeExceptionDetailsInFaults setting in the service's web.config to true:

<behaviors>
    <serviceBehaviors>
        <behavior name="TestServiceBehavior">
            <serviceMetadata httpGetEnabled="true"/>
            <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
    </serviceBehaviors>
</behaviors>

Adding the Silverlight Service Reference

Once your WCF service is running, you can add a reference from your Silverlight project. To do that, right click the Silverlight project and choose "Add Service Reference". Be sure to change the namespace to something useful, as this is the namespace that will be appended to the Silverlight application's namespace.

image

To avoid dealing with cross-domain configuration right at the moment, I added the service to the test web project. This ensures that the service and the Silverlight app are served from the same server and port.

image

Cross-Domain Support

If you want to enable your service to work cross-domain, there are two files you will want to concern yourself with:

crossdomain.xml

This file comes from back in the macromedia flash days. Since it has wide support on the internet, it makes sense for Silverlight to support it. You can read some documentation on this file format here.

One use supported by the specification is to specify domains which can access the service:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy 
  SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <allow-access-from domain="www.yoursite.com" />
  <allow-access-from domain="yoursite.com" />
  <allow-access-from domain="*.moock.org" />
</cross-domain-policy>

However, that detailed format is not supported by Silverlight. Instead, you must opt in to all domains if you want to use the crossdomain.xml approach. Such a policy file would look like this:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy 
  SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <allow-access-from domain="*" />
</cross-domain-policy>

However, since this format doesn't cover all the scenarios that Silverlight developers are likely interested in (and flash developers too, most likely), Microsoft has provided another cross-domain file that is more flexible and currently Silverlight-specific: clientaccesspolicy.xml.

clientaccesspolicy.xml

lf you want finer control over your cross-domain policy, you'll want to use clientaccesspolicy.xml. This file allows you to specify both what domains have access to your service, and also what subpaths are covered by the policy. You can include both clientaccesspolicy.xml and crossdomain.xml for your services; Silverlight will check this file before looking for the crossdomain.xml

The equivalent to the "allow all domains access to all services" is below:

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from>
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

If you want to get more selective about what you enable, you can specify external domains and subfolders on your site:

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from>
        <domain uri="http://contoso.com"/>
      </allow-from>
      <grant-to>
        <resource path="/public-services/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

You can find additional information about cross-domain files on msdn.

Ian Griffiths discovered a small bug with the Silverlight-specific policy file in Beta 1, and documented it here.

Calling Your Service

The proxy and default configuration generated when you add the service reference will be adequate for your initial testing. The generated .ClientConfig file has specifics about your service. If you change your service, and refresh your reference, this file will be updated to reflect those changes. If you run into any problems with that, just delete the file, remove your service references, and re-add them.

ServiceReferences.ClientConfig File
<configuration>
    <system.serviceModel>
        <client>
            <endpoint 
                address="http://localhost:47879/SilverlightApplication2_Web/Service.svc"
                binding="basicHttpBinding" 
                bindingConfiguration="BasicHttpBinding_IService"
                contract="SilverlightApplication2.ServiceReference1.IService"
                name="BasicHttpBinding_IService" />
        </client>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IService" 
                         maxBufferSize="65536"
                         maxReceivedMessageSize="65536">
                    <security mode="None" />
                </binding>
            </basicHttpBinding>
        </bindings>
    </system.serviceModel>
</configuration>

Note that the default endpoint has the address in the file. You can override this in code if you desire. However, since this is a configuration file, you'll probably want to change (and maintain) the endpoint address in the config here, prior to deploying your app to test/production. Note, that for beta 1, this file is not used. That will change in Beta 2 or RTM.

The clientconfig file is deployed in the xap.

Calling Code
private void TestService()
{
    ServiceReference1.TestServiceClient client = 
        new ServiceReference1.TestServiceClient();
    
    client.GetTheValueCompleted += 
        new EventHandler<TempBizAppDemo1.ServiceReference1.GetTheValueCompletedEventArgs>
            (client_GetTheValueCompleted);
    
    client.GetTheValueAsync();
}

void client_GetTheValueCompleted(
    object sender, 
    TempBizAppDemo1.ServiceReference1.GetTheValueCompletedEventArgs e)
{
    // act upon the information returned from the service
    // e.Result;
}

As you may already know, all service calls in Silverlight 2 Beta 1 are async calls. Getting data from a service is a two-step operation. First, you set up an event handler which will be called when the service completes. Second, you call the service async method.

There's a lot of controversy about requiring async calls in Silverlight, so it may be something that changes in the future. I'll make no value judgements on it here. However, the current async implementation has the broadest support across browsers and operating systems. It also helps to ensure that the UI thread is not blocked (the calls go through the browser network stack, which is typically on the UI thread), and the browser UI remains responsive. Since async calls are a requirement in Beta 1, we'll just take that as a given.

The nice thing is, once you are used to the async pattern, the code is nice. The event handler you get has strongly-typed event args that include the return value from your service in e.Result (there is no e.Result if your service is a void function or VB sub)

Next Steps

At this point, if you have some UI hooked up to view the results from your service, you should be all set to go ... assuming message-level security doesn't matter (and it doesn't in many many cases as proven by the proliferation of AJAX applications out there calling open services without any message-level security)

Security

When it came to dealing with security like I normally would in a WCF service-based application, I ran into a couple brick walls and ran out of time. I did learn about some other options that may work, though.

The current MSDN documentation on securing your services for Silverlight 2 Beta 1 clients can be found here. It's pretty light, though.

I'm still feeling my way around some of the security options available to us, including tokens and rolling custom encryption. Look for a follow-up post once that is all sorted out. In the mean time, continue to secure your services like you would for AJAX applications (transport-level, HTTPS, IIS-based auth for the entire application) and have your authentication happen outside of Silverlight. Developers coming from the desktop world and those currently doing service calls from ASP.NET server-side will likely not be satisfied with that, so I promise to provide some good examples in the future :)

Update: Tim Heuer also has a great post on this topic. Take a look at it when you finish up here. 

 

         
posted by Pete Brown on Wednesday, March 19, 2008
filed under:          

21 comments for “WCF Integration in Silverlight 2 Beta 1”

  1. Sergeysays:
    Hi,

    I create a virtual directory for web project, which uses Silvelright 2.0. I use a WCF Service for get data from server. There are exeption when I view site over virtual directory in browser. Can you help me?
  2. Pete Brownsays:
    Hi Sergey

    1. Is this a cross-domain issue? Is your service hosted on a machine other than what served up the Silverlight app? Are you using host headers or anything that would change the machine or domain name?

    2. If you rule out the above, check to make sure your service is using basicHttpBinding, not wsHttpBinding or tcp.

    3. When you generated the proxy to the service, did you get any error information then? If not, and the service interface hasn't changed, the exception might just be in the code server-side.

    Pete
  3. Andy Beaulieusays:
    Hi Pete!

    Is there any other trick to getting at the exception details in the silverlight client? I added includeExceptionDetailInFaults=true, and if I look at the http traffic coming back, I do indeed see the exception details in an InnerException. But I don't have access to them in the CompletedEventArgs.Error class - I always get a ServiceModel.ProtocolException, with InnerException being null.
  4. Robertsays:
    I created a silverlight app and a wcf service that returns a stream. I get a partial trust error when trying to return the stream to the silverlight app. How do I properly return a stream from a WCF service to silverlight. Is there anything special I have to do. Any help on this would be great thanks!!
  5. codismsays:
    In my application, it is ok to allow anonymous access to all end points. But the logic determining if a certain operation can be preformed is in code, by user information provided in a context object. Could you point me out the solution to pass user information automatically for each service call?
  6. shengsays:
    i have a wcf service,exe is can run.

    but it's can't run with silverlight.
    WCF services already set up to use basicHttpBinding

    错误如下:

    "System.ServiceModel.CommunicationException"类型的异常在
    System.ServiceModel.dll 中发生,但未在用户代码中进行处理

    其他信息: Operation is not valid due to the current state of the object.



    <configuration>
    <system.serviceModel>
    <bindings>
    <basicHttpBinding>
    <binding name="BasicHttpBinding_ILoginService" maxBufferSize="65536"
    maxReceivedMessageSize="65536">
    <security mode="None" />
    </binding>
    </basicHttpBinding>
    </bindings>
    <client>
    <endpoint address="http://tp0659/SCMISWCFHost/Login.svc" binding="basicHttpBinding"
    bindingConfiguration="BasicHttpBinding_ILoginService" contract="SilverlightLogin.SLLoginService.ILoginService"
    name="BasicHttpBinding_ILoginService" />
    </client>
    </system.serviceModel>
    </configuration>


    Can you help me?
  7. Jeremy Thakesays:
    I am using WCF services and can't see a way of setting a behavoiur on the client as you would in a config file e.g.:

    behavior name="XamlPrintWcfService.XamlPrintWcfIServiceBehavior"
    dataContractSerializer maxItemsInObjectGraph=
    "6553600"


    How can this be done in C# code within the Silverlight Project. Note I'm using Silverlight 2.0 beta 1. I noticed that is does create the ServiceReferences.ClientConfig but it simply doesn't use this and you have to set it in code using your above example for WCF (you state it works for ASMX).
  8. Pedrogovisays:
    Hello Pete,
    Sorry for my English, I am Spanish.
    My problem is that if I make a call to a method that has as a parameter a byte [] if the size is small works correctly but if the big threw exception Additional information: [UnexpectedHttpResponseCode]
    Arguments:Not Found
    Debugging resource strings are unavailable. Often the key and arguments provide sufficient information to diagnose the problem
    Thank you Pete.
  9. Pete Brownsays:
    @Pedrogovi

    There are limitations on size when working with SOAP. However, have you tried bumping up the message size setting in the clientconfig file (this only works in beta 2 and presumably in RTM. That may be enough.

    Pete
  10. borithsays:
    i have created a silverlight application that show the data from sql server 2005. when i add service reference, i get an error message saying that: an error occured:could not find service... make sure you provide the correct url....

    i don't know how to deal with that. could you point me out?
  11. Pete Brownsays:
    @borith

    Is the service on a different server or port? If so, did you add a Beta 2-compatible client access policy? The format changed very slightly from Beta 1. You now need the "-request-headers=" bit. However, that's a runtime issue, not somethign you'd normally run into when adding the service reference. Here's the updated format anyway:

    <?xml version="1.0" encoding="utf-8"?>
    <access-policy>
    <cross-domain-access>
    <policy>
    <allow-from http-request-headers="*">
    <domain uri="*"/>

    </allow-from>
    <grant-to>
    <resource path="/" include-subpaths="true"/>
    </grant-to>
    </policy>
    </cross-domain-access>
    </access-policy>

    If that's not the issue, is the service using SOAP 1.1 (asmx or WCF basicHttpBinding)? Does it use any authentication headers or anything like that? Do you have the URL correct? Can you enter the URL in the browser and get the service test page (if asmx)?

    Pete
  12. Jalilsays:
    I have created a wcf service and I can add service references to this service in silverlight project and can make a new instance of it :

    ServiceReference1.MySampleClient Client = new ServiceReference1.MySampleClient();

    but I don't access to its members:
    Client.DoCallCompleted : is accessible
    Client.DoCall() : isn't accessible
    -- DoCall() is a method in WCF service
    I will be grateful if you help me.
  13. Michaelsays:
    I have created a wcf service and I have added service references to this service in silverlight,but I have a warning that said: "Impossible loading one or more required types,for more information retrieval the LoaderExceptions property". Web Service method returns a string and have a type int parameter. Please someone help me!
  14. Pete Brownsays:
    @Michael

    Is the web service set to use basicHttpBinding? Did you use the "Silverlight-enabled Web Service" template, or did you create a regular WCF service? If regular, try creating it with just the silverlight template, as that takes the guesswork out of the changes.

    If that doesn't help, please post your question over on the forums at http://silverlight.net. There are tons of folks there eager to help out.

    Pete
  15. Gaurav Mantrisays:
    My question is because of this asynch framework for invoking web services in Silverlight 2, how do I bind the results to a dynamically generated control. Say I have a TreeView control (I am using Telerik's RAD controls for Silverlight) with 4 TreeView items in it. Upon clicking of each item, I invoke same service which returns me data specific to the treeview item clicked. Since all the clicks call the same function, they obviously will have same call back function. How do I know in my callback function that the data I got was a result of clicking on which treeviewitem?
  16. Vitthalsays:
    i have created an application in silverlight 3 beta
    i am trying to access a dataset from Silverlight enabled
    WCF. i have created the WCF.i have added its reference to the silverlight app. but the ServiceReferences.ClientConfig file shows error
    "ServiceReference1.ArrayOfXElement not declared" in the code the code is as follows-

    Partial Public Class DoWorkResponse
    <System.ServiceModel.MessageBodyMemberAttribute([Namespace]:="", Order:=0), _
    System.Xml.Serialization.XmlElementAttribute(IsNullable:=True)> _
    Public DoWorkResult As ServiceReference1.ArrayOfXElement

    Public Sub New()
    MyBase.New
    End Sub

    Public Sub New(ByVal DoWorkResult As ServiceReference1.ArrayOfXElement)
    MyBase.New()
    Me.DoWorkResult = DoWorkResult
    End Sub
    End Class


    And my ServiceReferences.ClientConfig file is empty

    even there is problem in creating proxy object as

    Dim proxy As ServiceReference1.MyWebServiceClient = New ServiceReference1.MyWebServiceClient()
    AddHandler proxy.DoWorkCompleted, AddressOf proxy_DoWorkCompleted

    on first line it shows error that key is not found in dictionary.

    thats why iam not getting the property
    " e.Result " to collect the value returned from web service

    Please help me.i have stucked here since last two days.
    Thanks in advance
  17. Pete Brownsays:
    @Vitthal

    In general, datasets are not a good thing to return from SOAP services as they are very heavily platform dependent. You'll have a hard time using them in other languages, from ajax and from RIA technologies. Instead, return strongly typed classes that contain the fields as strongly typed member variables with public gets/sets

    Silverlight actually requires this as it has no concept of a dataset.

    You can also use something like ADO.NET Data Services or .NET RIA Services to surface a model of your database (created using entity framework, linq to sql etc.) to cut down on the number of classes you'd need to write.

    Pete

Comment on this Post

Remember me

5 trackbacks for “WCF Integration in Silverlight 2 Beta 1”

  1. Christopher Steensays:
    ASP.NET ASP.NET Basics: Foundation of ASP.NET [Via: mikedopp ] Miscellaneous DotNetNuke 4.8.2 Released!...
  2. Silverlight SDKsays:
    I've been watching the Silverlight forums since the release of Silverlight 2, and I've noticed folks
  3. Silverlight SDKsays:
    I've been watching the Silverlight forums since the release of Silverlight 2, and I've noticed folks