MVVM Light template and WCF services (or any ASP.NET application for that matter)

.NET, MVVM, Silverlight, Technical stuff, WCF, Work
See comments

I was recently made aware of a couple of people having issues with WCF services (or ASP.NET applications) when using the MVVM Light project template for Silverlight. There is a blog post and a StackOverflow question, so what exactly is happening there?

Well in fact it is pretty simple when you know how Silverlight connects to web services. Due to the security model of Silverlight, the application cannot connect to a web site if it is not originating of this very website. In laymen’s terms, it means that the application can only connect to a web server if it also comes from the same webserver. For example, if the Silverlight application is served by http://www.galasoft.com, it won’t be able to connect to, say, http://www.cnn.com without getting an exception. We talk about cross-domain access restrictions.

Of course there are ways around that, for instance a website can specifically give access to Silverlight applications through a configuration file.

In the case that concerns us, it is exactly what is happening. You see, the MVVM Light Silverlight project template creates a Silverlight application without an ASP.NET host. I didn’t add one because I didn’t want to complicate the template too much. And also, to be honest, because adding a web project is super easy, but of course only if you know how to do, and this is exactly what we will do here!

Creating the application

The steps to create the application and the WCF service are as follows:

  • Create the MVVM Light application using the MVVM Light project template for Silverlight.
  • Right click on the solution in the Solution Explorer and select “Add, New Project from the context menu.
  • From the WCF category, select a WCF Service Application and create it.
  • Build the application.
  • Right click on the MVVM Light project and select Add Service Reference from the context menu.
  • In the Add Service Reference dialog, click on Discover.
  • Make sure that the found service is the one you want to connect to, and click on OK.
  • In the MVVM Light application, open the file Model/DataService.cs and modify the code as follows:
public class DataService : IDataService
{
    public void GetData(Action<DataItem, Exception> callback)
    {
        var client = new ServiceReference1.Service1Client();
        client.GetDataCompleted += ClientGetDataCompleted;

        client.GetDataAsync(1234, callback);
    }

    void ClientGetDataCompleted(
        object sender, 
        ServiceReference1.GetDataCompletedEventArgs e)
    {
        var callback = e.UserState as Action<DataItem, Exception>;

        if (callback == null)
        {
            return;
        }

        if (e.Error != null)
        {
            callback(null, e.Error);
            return;
        }

        callback(new DataItem(e.Result), null);
    }
}

This code uses an asynchronous service call pattern where the callback (a reference to a method passed as Action by the caller) is saved in the service call. Then when the asychronous call returns (in the Completed event), the callback is retrieved from the UserState. If an error occurred, the callback can be used to pass this error to the caller. Otherwise, a new DataItem class is created and passed to the called.

Details about this pattern can be found in my talks Understanding the MVVM pattern and Deep Dive MVVM.

So why doesn’t it work?

If you run the application now (making sure that the MVVM Light project is selected as Startup Project), you will first see a warning dialog and then an exception. What happened?

What happened here is exactly the cross-domain issue I mentioned: The Silverlight application is not explicitly hosted into a web application, so Visual Studio is using an auto-generated HTML page instead, and running it from the embedded web server (“Cassini”). In fact, unless you explicitly specified otherwise, the test page is run in “file mode” instead of HTTP mode. The URI in the web browser window starts with C:\ for instance, instead of http://.

In those conditions, the access to the WCF service is (justly) denied, and you get a security exception.

Correcting the error

In order to correct the error, you can either add a cross-domain policy file to your WCF application, or host the Silverlight application in the same web project as the WCF service. Let’s do that now:

  • Right click on the WCF Service application in the Solution Explorer, and select Properties from the context menu.
  • On the left, select the Silverlight Applications tab.
  • Click on Add.
  • Select Use an existing Silverlight project in the solution and make sure that your MVVM Light application is selected in the combo box.
  • Make sure that Add a test page that references the control is checked, as well as Enable Silverlight debugging.
  • Press Add.

This creates two new files in your WCF Service application: One is suffixed TestPage.html and the other is suffixed TestPage.aspx. We typically don’t need the ASPX one so you can safely delete it.

  • Right click on the WCF Service application and select Set as StartUp Project from the context menu.
  • Right click about the HTML test page and select Set as Start Page from the context menu.

By doing this, we force Visual Studio to serve the HTML page and the Silverlight application from the same URL as the WCF Service, and to serve it as HTTP. This is possible because a WCF Service application is nothing else than an ASP.NET web server running WCF. So the same web server can deliver HTML, Silverlight and of course WCF.

If you run the application now, you will see the following result:

Note that the warning shown earlier will still appear, but it is really just a warning, and you can safely turn it off. Just remember the cross-domain restriction when you publish your Silverlight application to another web server!

Success, we hit the WCF service and returned with a valid result! In fact, you can verify by placing breakpoints in the DataService.GetData method on the client, and in the Service1.GetData method on the server. Then press F5 to run the application in Debug mode and notice how you can easily debug both the client and the server at the same time.

What about the opposite way?

It is also possible to add a new MVVM Light application to an existing WCF Service application with the following steps (I am going a bit faster now, I am sure you will get it easily):

  • Open the existing WCF Service application.
  • Right click on the Solution and select Add New Project.
  • Select the MVVM Light project template for Silverlight and add a new project.
  • Build the application.
  • Just like before, add a Service Reference to the Silverlight application.
  • Again, just like before, add the Silverlight application to the WCF Service application (WCF Project Properties, Silverlight Applications, Add…).

Summary

Hopefully the confusion around this is cleared now. In summary:

  • Silverlight applications are restricted to access cross-domain web servers (unless explicitly authorized to do so).
  • The default MVVM Light application does not have an explicit host, so it runs off the default test page.
  • You can however easily add the MVVM Light application to the WCF Service application and then run it and debug it.

Note that the steps above can be used to add an MVVM Light application (or any Silverlight application) to any ASP.NET application, be it WCF Service application, ASP.NET MVC application or “classic” ASP.NET application.

Happy coding!
Laurent

Previous entry | Next blog entry

Comments for MVVM Light template and WCF services (or any ASP.NET application for that matter)