JM

Adventures in Software Craftsmanship

iOS, JavaScript, XAML, HTML5, CSS

Upgrading to WCF RIA Services & ASP.NET MVC 2

Recently we upgraded our Silverlight 3 application to Silverlight 4, WCF RIA Services, and ASP.NET MVC 2.  For the most part, the upgrade went very smoothly.  However RIA Services just quit working.  We were getting “Load operation failed for query ‘GetAbcd’. The remote server returned an error: NotFound.”  It turns out this is a fairly simple thing to fix, but lets walk through how to do it!

Start with a very simple Player Entity.

Player EDMX

Then lets create a RIA service to retrieve all of the Players in our database.

namespace MvcApplication1
{
  using System.Data;
  using System.Linq;
  using System.Web.DomainServices.Providers;
  using System.Web.Ria;

  [EnableClientAccess()]
  public class DomainService1 : LinqToEntitiesDomainService
  {
  	public IQueryable GetPlayers()
  	{
  		return this.ObjectContext.Players;
  	}

  	...
  }
}

In our Silverlight application lets make a call to GetPlayers() to populate a ListBox.

namespace SilverlightApplication1
{
  using System.Collections.ObjectModel;
  using System.ComponentModel;
  using System.Windows.Controls;
  using MvcApplication1;

  public partial class MainPage : UserControl
  {
  	private ObservableCollection players;

  	public MainPage()
  	{
  		InitializeComponent();

  		DomainService1 service = new DomainService1();

  		var query = service.GetPlayersQuery();

  		service.Load(
  			query,
  			result =>
  			{
  				if (!result.HasError)
  				{
  					players = new ObservableCollection(result.Entities);

  					this.DataContext = players;
  				}
  			},
  			null);
  	}
  }
}

Running the application we get the expected error:

“System.Windows.Ria.DomainOperationException: Load operation failed for query ‘GetPlayers’. The remote server returned an error: NotFound.”

Looking at the service call in Firebug you can determine where it’s trying to call the service.

SilverlightApplication1 - Mozilla Firefox (2)

So RIA Services is trying to access http://localhost:54471/ClientBin/MvcApplication-DomainService1.svc and is unable to find it.  So what’s going on here?

Saurabh Pant has an awesome in-depth post describing what RIA Services is doing.  Essentially what it should be doing is dynamically generating a .svc file for the service.  If you run this exact same code in a ASP.NET Web it works perfectly; ASP.NET MVC is what’s giving us problems.

An excerpt from Saurabh’s post:

“By default DomainServices do not have a physical .SVC file generated for them at Design Time.

However each DomainService has a virtual .SVC associated with it. The .SVC represents the WCF Service that services requests for that particular DomainService. For a given DomainService the path to its .SVC can be determined using the following convention:

[SilverlightApplicationBaseURI] + [DomainServiceFullName].svc (With all “.” replaced by “-“)

So HRApp.Web.OrganizationService is exposed as –http://[ApplicationBaseURI]/HRApp-Web-OrganizationService.svc

At RunTime when the first request (within a particular Application Domain) is made for a DomainService’s .SVC file , the registered httpModules intercept the call and RIA Services writes out an in memory .SVC file on the fly.

A request for the DomainService .SVC in any folder under the Web Application root is redirected to [WebAppRoot]/Services/[DomainService].svc using ASP.net URL rewriting. The service is thus accessible under any folder in the WebApp. Hence even if a .XAP is moved around under the hosting Web App Root, the Silverlight App’s relative reference to the DomainService is not broken.

NOTE – If a physical .svc file with the right file name (as per convention) is present in the ~/Services folder, that is used to define the Service Contract and no virtual .SVC file is generated.”

So essentially, all we need to do is create a .svc file in the /Services folder with the correct name (MvcApplication-DomainService1.svc in this instance).

MvcApplication-DomainService1.svc

<%@
  ServiceHost
  Service="MvcApplication1.DomainService1"
  Factory="System.Web.Ria.DomainServiceHostFactoryEx"
%>

DomainServiceHostFactoryEx.cs

namespace System.Web.Ria
{
  using System.Linq;
  using System.ServiceModel;
  using System.Web.Ria.Services;

  public class DomainServiceHostFactoryEx : DomainServiceHostFactory
  {
  	protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
  	{
  		return new DomainServiceHost(serviceType, FilterAddresses(baseAddresses));
  	}

  	private static Uri[] FilterAddresses(Uri[] baseAddresses)
  	{
  		return baseAddresses.GroupBy(uri => uri.Scheme).SelectMany(uri => uri).ToArray();
  	}
  }
}

The DomainServiceHostFactoryEx factory class handles routing  the call to our service.  Note that if you’re having problems running RIA Services on a live server with multiple host headers, using this technique is also your solution (for any WCF Service).  Doing this sort of custom HostFactory is a long running solution to a limitation in how WCF handles multiple host headers.

Your solution should now look like this:

WindowClipping (5)

You can also now browse to the Service and see that it’s running.

DomainService1 Service - Windows Internet Explorer (2)

And when we run the application we have success!

SilverlightApplication1 - Google Chrome

I hope that helps!

Download Source

13 Comments

  1. [...] going to start with where we left off last time.  This solution loads a list of players into a ListBox using RIA [...]

  2. OK, This was a HUGE help and worked great and got me past a HUGE hurdle.

    I am, however, still confused about one item.

    Can you comment on the following:

    > A request for the DomainService .SVC in any folder under the Web Application root is redirected to
    > [WebAppRoot]/Services/[DomainService].svc using ASP.net URL rewriting. The service is thus accessible
    > under any folder in the WebApp. Hence even if a .XAP is moved around under the hosting Web App Root, the
    > Silverlight App’s relative reference to the DomainService is not broken.

    Where should I put my .svc file? It was not finding it in /services/ folder so I moved it to be in the same folder as my .xap file. I figure this is because in my SL code I am using UriKind.Relative.

    I am totally confused about where UrlRewriting comes in (web server, web.config, C# code).

    Finally, what in all of this is the GO-FORWARD method I should look to find when everything works nicely w/o this extra code….or is this extra code the go-forward method?

    Thanks a million,
    Rex

  3. Hey Rex,

    I’m glad this was able to help you out.

    I’ve had no problem with putting my services in the /services folder. What do you mean by using UriKind.Relative in your code? Are you explicitly assigning the service location in your code? One other thing you may want to make sure you have is the entries in the web.config file.

    <system.web>
            <httpModules>
                <add name="DomainServiceModule" type="System.Web.Ria.Services.DomainServiceHttpModule, System.Web.Ria, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
            </httpModules>
    </system.web>
    
    <system.webServer>
            <modules runAllManagedModulesForAllRequests="true">
                <add name="DomainServiceModule" preCondition="managedHandler" type="System.Web.Ria.Services.DomainServiceHttpModule, System.Web.Ria, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
            </modules>
    </system.webServer>

    You can sort of ignore the UrlRewriting bit if it’s confusing you. RIA services is always checking for the service under /Services folder. What Saurabh is trying to say is that the call from the Silverlight Application in the XAP should find the service, regardless of where the XAP resides on the server.

    As of now, yes this is the “go-forward” method to publishing your RIA services. To get RIA Services running on a live server, there is a LOT of extra configuration that currently has to be done, including this extra code. See Tim Heuer’s blog for more info on this. Microsoft has heard these complaints though and is looking into making the deployment story of RIA services better by the time it releases.

  4. Man….I have no word to thank you. You have just saved me another day.
    I have just started looking at silverlight and RIA Services and once i published a test application on UAT server, i got this bizzare error. I have tried almost everything yesterday but no luck at all.

    I wish i would have discovered your post earlier.
    Tons of thanks. I have already bookmarked your site.

  5. Glad I was able to help!

    Joe

  6. Social comments and analytics for this post…

    This post was mentioned on Twitter by perepechin: RIA Services and ASP.NET MVC 2 – http://bit.ly/aG5EpJ this trick works well #silverlight #aspnetmvc…

  7. Is the .svc file trick still required now that VS2010 and SL4 are released?

    Hopeful, Rex

  8. Hey Rex,

    Yes as far as I know you still need to use the .svc file trick when working with ASP.NET MVC and RIA Services in RTM.

    Joe

  9. Hi Joe,

    THanks for this post, it helped me out. In VS2010 / SL4, some references a different.
    Instead of System.Web.Ria.Services you need:

    using System.ServiceModel.DomainServices.Hosting
    + add reference System.ServiceModel.Activation

    Thanks again,
    Geert

  10. Welcome and thanks for the update!

  11. Thanks for posting this, it looks like it’s been really helpful to people!

    There’s actually a simpler solution though. You don’t need to have the physical .SVC file. I put all of my DomainServices insider a /Services folder, and then I always reference them through that folder. I don’t have any Controllers that would be routed through a /Services URI, so I can then use routes.IgnoreRoute(“Services/{*pathInfo}”) within my Global.asax.cs file, and I’m done – everything works for me.

    I hope this works for you too.

    -Jeff

  12. [...] to Vote[FriendFeed] Upgrading to WCF RIA Services & ASP.NET MVC 2 « joe.mcbride (5/10/2011)Tuesday, May 10, 2011 from [...]

  13. This works well when you have Service Libraries in another project. Good work!!

RSS feed for comments on this post.

Leave a Response

Latest from Twitter