OData Apps in LightSwitch (With Custom RIA Data Sources and Many to Many!), Part 3
Update again: Attaching to a database to many to many relationships is now supported (in Visual Studio 2012 RTM) and the relationships will display in the entity designer just like any other relationship. As a result it's possible to do the below scenario without a RIA Data Source and instead just use LightSwitch built in functionality.
Update: I've posted the sample application in the MSDN Code Gallery here - http://code.msdn.microsoft.com/Consuming-OData-in-7df2ef7b
To refresh everyone on how we got here, we started with the idea that we would use some new features in LightSwitch for Visual Studio 11 (Beta) that would allow us to attach to an OData service. Specifically we are attaching to the Commuter API OData service.
We wanted to solve 4 basic problems:
- Where is my stop? (we solved this in Part 1 and 2 with a Bing maps extension)
- When is my train arriving? (we solved this in Part 1)
- How do I get there? (we solved this in Part 1 and 2 with Route information and maps)
- How many escalators will be broken today? (we saved this problem for Part 3)
To find a solution to this last problem we are going to pull in data from the “Incidents” entity in the OData service. This entity contains all the information regarding broken escalators and things of that nature.
Retrieving “Incidents” data
If we open up our OData app and look at the Incidents entity in the Entity Designer, it should look like this:
Note: If you don’t have the Incidents entity in your entity designer, then right click on the TransitDataData data source and select “Update data source”. Then you will be able to import the Incidents entity.
You can see we don’t have any relationships on this entity. There are indeed relationships defined on this entity in the OData Service, but they are all “many to many” relationships. And LightSwitch does not provide support for many to many relationships out of the box.
That’s too bad, but it’s not a deal breaker. I would really like to relate the Incident data with our Stops data so that on our Stops List and Detail screen we could show all the incidents related to that stop (for example, the Metro Center stop has a broken escalator, and trains are all delayed because of snow).
Fortunately for us we can still make this work and pull in information for the Incidents entity and relate it to a specific Stop by creating a Custom RIA Data source. In general, Custom RIA Data Sources are beneficial for a wide array of problems, like aggregating data. Our custom RIA data source is going to enable us to create our own query to pull in the Incidents data from the Transit Data OData service and relate it with a corresponding Stop.
Custom RIA Data Source – Creation
Let’s get started on making our basic Custom RIA data source.
First of all, make sure, in Visual Studio, under Tools –> Options –> Projects and Solutions that you have the “Always show solution” checkbox checked.
Now, right click on your solution and select Add –> New Project.
Let’s just add a “Class Library” (I’m using a C# Class Library because my LightSwitch project is a C# project). Call the new project “RiaService” and click OK.
In our new project, double click on the “Properties” icon and change the Target Framework to .NET Framework 4.0. This will be important later on when we try to attach to our Custom RIA data source (I’ll explain when we get there).
Now right click our new project and select Add –> New Item. Select Visual C# Items –> Web –> Domain Service Class. Call it TransitData and hit OK. The only reason why I chose this file type is because it automatically adds a bunch of project references that we are going to need (you certainly could have just selected a basic Class file and added the references yourself). We still need to add one more reference to our project. So right click the References node and select Add Reference. Add a reference to System.Data.Services.Client.dll.
Your list of References should look like this:
Now we need to add a service reference to the Transit Data OData service. Right click the References node again and select “Add Service Reference” and use this OData service - http://transit.cloudapp.net/DevTransitODataService.svc (this is of course the same one that we attached to with our LightSwitch application in Part 1). Call the namespace TransitDataServiceReference. It should look something like this -
Once you hit OK it will attach to that OData service and generate code for the corresponding entities in the service. You’ll see now in a minute how we have an Incidents entity available to us in our custom RIA data service project. It’s important to understand that we are in NO way using any code right now from our LightSwitch project, or am I referring to the Incidents entity that is available in our LightSwitch project under the TransitDataData source. The Transit Data service that we just attached to and the Incidents entity that we have generated code for is specific to this RIA project.
At this point we have a basic custom RIA data service. We just have to add a bit of code at this point before we can add the RIA service to LightSwitch as a data source.
Custom RIA Data Source – Adding Code
Let’s add some code now to the TransitData.cs file. Copy and paste the below code into your file:
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.ComponentModel.DataAnnotations;
- using System.Linq;
- using System.ServiceModel.DomainServices.Hosting;
- using System.ServiceModel.DomainServices.Server;
- using RIAService.TransitDataServiceReference;
- namespace RIAService
- publicclassTransitDomainService : DomainService
- [Query(IsDefault = true)]
- publicIQueryable<Incident> GetIncidents()
- publicIQueryable<Incident> GetIncidentsByStop(string stopId)
- TransitData myTransitData = newTransitData(newUri("http://transit.cloudapp.net/DevTransitODataService.svc"));
- return myTransitData.Stops.Where(s => s.StopId == stopId).SelectMany(s => s.Incidents);
- namespace RIAService.TransitDataServiceReference
- publicstring IncidentId
That’s it for code that we’ll need to write. Now let’s go over it:
Note how we have a “using TransitDataServiceReference” statement at the top. When we generated code for our service reference that we added we put all that code under the TransitDataServiceReference namespace. This will allow us to directly reference the Incident entity and the TransitData class.
For a custom RIA data service we need to have a default query type which is what the GetIncidents() method is. This query would typically be the “All” query or the query that returns all of the records for the entity. For our purposes we don’t need to do anything more than just return null here.
The GetIncidentsByStop() method we define as a “Query” as well. This means that when we attach to this RIA Data service through LightSwitch that this query will show up in the Query designer. This is really the query that’s going to do all our work. The code in this method is:
- Creating a new instance of the TrainsitData data service (for which you have to pass in the Uri to the actual OData service)
- Querying on all of the “Stops” and retrieving all the Incidents that have a “StopId” corresponding to the “stopId” that was passed into the method. (For example, if we pass in the Metro Center stop it will retrieve all the Incidents that are related to that stop).
- Those incidents related to the stopId are then returned
At the bottom of the code snippet you’ll notice that we have a separate class. The Incidents entity that was generated does not have a primary key defined on it. We are going to need one before we try to attach to this RIA data service with LightSwitch. So we need to specify which field is the primary key, which is all we are doing here.
Extending upon the “Incident” class that was generated we define an inner class called Metadata. Then we specify that the IncidentId property is the primary key by giving it the “Key” attribute.
More information on partial classes: Partial Classes and Methods (C# Programming Guide) and Partial (Visual Basic)
More information on creating a metadata class for RIA services: How to Add Metadata Classes
More information on the “Key” attribute: A guide through WCF RIA Services attributes
Add Our RIA Data Source
Go ahead and build our RIAService project. Remember we made our RIA project target the .Net 4.0 framework? That was important otherwise you will get an error when trying to attach to it here, since the LightSwitch project is also targeting the .NET 4.0 Framework.
Open up the LightSwitch project, and let’s add this data source to it, like so:
If the RIA service doesn’t show up here, then select Add Reference –> Solution –> Projects and select the RIAService project.
Click Next, select all the entities and queries (there should only be the Incidents entity and GetIncidentsByStop query).
We should now have the data source, entity and query in our designer:
Modify our Stops List and Detail Screen
We’ll need to modify our Stops List and Detail screen now to include the Incidents information that we want (my screen is specifically called StopsDCMetroListDetail).
Double click on our screen to bring it up in the screen designer. With the screen designer open, select “Add Data Item…” and select the GetIncidentsByStop query like so:
Hit OK and it will be added to the screen designer.
Now drag and drop the data for GetIncidentsByStop onto our screen so that it looks like this:
Now we’ll need to bind our stopId query parameter to whatever Stop we currently have selected on our screen.
So, double click on the stopId query parameter, and then in the “Properties” window bind it to StopsDCMetro.SelectedItem.StopId so that it looks like this:
This effectively says that the “stopId” query parameter is going to be equal to whatever Stop we currently have selected on our screen.
And that is it! Save it, and re-build the solution.
F5 it and open up the Stops DC Metro List and Detail screen. You should see a list of Incidents beneath the Details for the Stop. So it will look something like this now:
You can see in this instance that not even the Pentagon is immune to the ubiquitous broken escalator problem.
Wrapping it Up
To summarize what we’ve done here, I showed you how to create a RIA Service wrapper around a data source that has many to many relationships. We specifically utilized this RIA Service to relate data between the Stops and the Incidents entity (so that we can tell how many escalators will be busted at our Metro stop).
That pretty much does it for our OData sample app. I will be publishing all this code (C# and VB.NET) out on the MSDN Code Gallery and will update the blog as soon as I have that done.
Please let me know if you have any questions or requests for future topics.