×

First time here?

You are looking at the most recent posts. You may also want to check out older archives. Please leave a comment, ask a question and consider subscribing to the latest posts via RSS or email. Thank you for visiting!

As we’ve all been guilty, when you see demonstrations of technologies most of the time the data samples show single table solutions.  When was the last time you’ve developed a single-table system? :-)  Thought so.

In RIA Services demonstrations, most of them have been single-table samples as well.  So how do you go about retrieving relational data (master/details type) with RIA Services?  Here’s an option.  I’m using VS2010, Silverlight 4 and the WCF RIA Services preview using the below sample.  I’m also using the Chinook sample database which has become one of my favorite simpler relational data samples to use.

Creating your project and associated RIA Services

This is easy, create a new Silverlight project and make sure the ‘Enable .NET RIA Services’ link is checked (yes, we know it doesn’t say WCF in that dialog).  My Silverlight application will be a simple button to retrieve artists then show their associated albums.  Here’s my XAML to start:

   1: <Grid x:Name="LayoutRoot" Background="White">
   2:     <StackPanel Width="400">
   3:         <Button Content="Get Artist Information" x:Name="GetArtistButton" Click="GetArtistButton_Click" />
   4:         <StackPanel Orientation="Horizontal">
   5:             <StackPanel x:Name="ArtistsContext">
   6:                 <StackPanel Orientation="Horizontal">
   7:                     <TextBlock Text="Artists: " />
   8:                     <TextBlock Text="{Binding ElementName=ListOfArtists, Path=Items.Count}" />
   9:                 </StackPanel>
  10:                 <ListBox x:Name="ListOfArtists" Width="200" Height="300" DisplayMemberPath="Name" ItemsSource="{Binding}"/>
  11:             </StackPanel>
  12:             <StackPanel x:Name="AlbumsContext">
  13:                 <StackPanel Orientation="Horizontal">
  14:                     <TextBlock Text="Albums: " />
  15:                     <TextBlock Text="{Binding ElementName=ListOfAlbums, Path=Items.Count}" />
  16:                 </StackPanel>
  17:                 <ListBox x:Name="ListOfAlbums" DisplayMemberPath="Title" ItemsSource="{Binding}" Width="200" Height="300"/>
  18:             </StackPanel>
  19:         </StackPanel>
  20:     </StackPanel>
  21: </Grid>

Now on the server side I need to create the associated models and domain services to be consumed.  I’m creating my model using Entity Framework and it looks like this:

Chinook Entity Model

Now I need to create my Domain Service class for that model (remember to build the solution after you create your model so it will show up in the tools).  When we create the Domain Service class be sure to enable the checkbox to generate associated classes for metadata.  Once we finish we have some stub services created for us. 

Using the Domain Service functions

We have GetArtists and GetAlbums functions we can work with.  As an example we can wire up the button click to retrieve a list of artists using the default functions we got:

   1: ChinookContext ctx = new ChinookContext();
   2:  
   3: private void GetArtistButton_Click(object sender, RoutedEventArgs e)
   4: {
   5:     ArtistsContext.DataContext = ctx.Artists;
   6:     ctx.Load(ctx.GetArtistsQuery());
   7: }

But what about when a user clicks on an Artist, we want to show the albums for that artist and not the others.  We need to modify our Domain Service to add a function:

   1: public IQueryable<Album> GetAlbumsForArtist(int ArtistId)
   2: {
   3:     return this.ObjectContext.Albums.Where(a => a.ArtistId == ArtistId);
   4: }

Now we can use that function when a user clicks on an associated artist to populate the album information:

   1: private void ListOfArtists_SelectionChanged(object sender, SelectionChangedEventArgs e)
   2: {
   3:     ListBox theList = sender as ListBox;
   4:     Artist a = theList.SelectedItem as Artist;
   5:     ctx.Albums.Clear();
   6:     AlbumsContext.DataContext = ctx.Albums;
   7:     ctx.Load(ctx.GetAlbumsForArtistQuery(a.ArtistId));
   8: }

Cool.

However, the second event handling for our master-details section for this particular data set seems unnecessary.  After all, why not just include the children data with our initial request if we *know* that we’re doing an explicit master-details view (and our set is not that large relatively speaking).

Modify the metadata classes

Remember the generated metadata classes?  Go back to it now.  You’ll see a definition of the ArtistMetadata that includes this:

   1: public EntityCollection<Album> Albums;

Notice it has an Albums collection property.  Great, so we could just modify our XAML binding to use some element binding and get the Albums property of the SelectedItem right?  Well, not yet.  If we do that, we’ll have no data.  Why is that?  Because we haven’t told RIA Services to perform the necessary additional query to get the data.  Simple add [Include] at the top of the Albums collection:

   1: [Include]
   2: public EntityCollection<Album> Albums;

And that’s what we need.  Now we can add a function to our Domain Service class to get the additional data:

   1: public IQueryable<Artist> GetArtistsWithAlbums()
   2: {
   3:     return this.ObjectContext.Artists.Include("Albums");
   4: }

Now we just need to do some clean up.  We need to change our button click code to get the GetArtistsWithAlbums query now instead of the other one first.

Remove unnecessary code and use binding to help us

Now we can remove the SelectionChanged event handler for our Artists ListBox as well as add some binding commands to our XAML like this:

   1: <Grid x:Name="LayoutRoot" Background="White">
   2:     <StackPanel Width="400">
   3:         <Button Content="Get Artist Information" x:Name="GetArtistButton" Click="GetArtistButton_Click" />
   4:         <StackPanel Orientation="Horizontal">
   5:             <StackPanel x:Name="ArtistsContext">
   6:                 <StackPanel Orientation="Horizontal">
   7:                     <TextBlock Text="Artists: " />
   8:                     <TextBlock Text="{Binding ElementName=ListOfArtists, Path=Items.Count}" />
   9:                 </StackPanel>
  10:                 <ListBox x:Name="ListOfArtists" Width="200" Height="300" DisplayMemberPath="Name" ItemsSource="{Binding}"/>
  11:             </StackPanel>
  12:             <StackPanel x:Name="AlbumsContext" DataContext="{Binding ElementName=ListOfArtists, Path=SelectedItem}" >
  13:                 <StackPanel Orientation="Horizontal">
  14:                     <TextBlock Text="Albums: " />
  15:                     <TextBlock Text="{Binding ElementName=ListOfAlbums, Path=Items.Count}" />
  16:                 </StackPanel>
  17:                 <ListBox x:Name="ListOfAlbums" DisplayMemberPath="Title" ItemsSource="{Binding Albums}" Width="200" Height="300"/>
  18:             </StackPanel>
  19:         </StackPanel>
  20:     </StackPanel>
  21: </Grid>

Notice how the DataContext of my Albums ListBox is now set using element binding to the SelectedItem of the Artists ListBox.  Then the ItemsSource of the ListBox for Albums has a {Binding Albums} command.  This is because our Artists query now includes the associated Album data and we can just reference the property.

Use with caution

While this example shows how easy it can be to have included results in your Domain Service query result, be mindful of when you are using.  For instance if you have a customer database of 1000 customers and you want all orders to be retrieved…it might not be wise to use this particular type of method. 

This presents merely another choice for areas where you may want/need it (i.e., country/state/city) for your application.

You can download the sample solution for the above code snippets here: SilverlightApplication41.zip.  Reminder that you will need to have the Chinook database installed already – it is NOT included with this sample download.

Hope this helps!


This work is licensed under a Creative Commons Attribution By license.


1/5/2010 9:37 PM | # re: RIA Services and relational data
Excellent post and thanks for thinking of a 1/2 way real application scenario vs a simple technology demo.
1/5/2010 11:15 PM | # re: RIA Services and relational data
Hi
What about using a stored proc ?
1/5/2010 11:18 PM | # re: RIA Services and relational data
Great timing, I was just watching your video on RIA services the other day and thinking about best practices for dealing with relational data. Thanks for filling the gap.
1/6/2010 12:28 AM | # re: RIA Services and relational data
Thanks for the multi-table example but I second the call for Stored Procedure examples. Not just simple retrieval but inserts, updates and deletes as well.
1/6/2010 1:00 PM | # re: RIA Services and relational data
Good post. I wish this had been written earlier as I spent a long time surfing around to figure this out. Another rarely mentioned issue which is worth going over is how to do many-to-many and include the results with RIA. The Entity Framework doesn't auto-generate a compatible model and RIA is very picky about exactly how you need to fix it.
1/6/2010 1:27 PM | # re: RIA Services and relational data
Hello, this is nice post, but I also miss "many-to-many". How can I do it with RIA? Is there some article on net on this problem? Thanks.
1/6/2010 2:40 PM | # re: RIA Services and relational data
When you call this.ObjectContext.Artists.Include("Albums") are those album objects stored in MyDataContext.Albums?
1/6/2010 3:19 PM | # re: RIA Services and relational data
John, you'll notice the function is IQueryable<Artist> GetArtistsWithAlbums. So when that function is called you'd get a list of Artist, which would include their Artist.Albums property where .Albums is EntityCollection<Album>. Make sense?
1/7/2010 4:03 PM | # re: RIA Services and relational data
great post
but i am looking for more
how to insert update and delete

also many to many sample
I need these things badly

thank u
1/8/2010 1:59 PM | # re: RIA Services and relational data
This is a bit off topic, but do you know if it's possible to run .NET Ria Services (not WCF ria Services) in VS10 beta? I am trying to do this because I cannot upgrade to WCF at the moment (my hosting does not support it) but I would like to use VS 10 for development.

My experiance is that after a project upgrade (keeping the framework at 3.5) in VS 10, I have reference errors that I cannot resolve. In particular, "Ria does not exist in the namespace 'System.Web' yet the reference to System.Web.Ria is there with a version of 2.0., as well as DomainServices and DataAnnotations.

If this can't be done, that's ok, but I thought I would give it a shot.

Greg
1/8/2010 2:12 PM | # re: RIA Services and relational data
Greg - no it is not possible.
1/8/2010 7:48 PM | # re: RIA Services and relational data
LOL, ok well thanks anyway, I can stop trying now...
1/10/2010 3:10 AM | # re: RIA Services and relational data
Tim,

Great article. Do you know if it would be possible to 'include' Navigation properties on an object like Artist? That is, if Artist were to have a Navigation Property called 'ExHubbyCollection", could you include the ExHubbyCollection in the initial call?
Gravatar
1/11/2010 8:23 AM | # re: RIA Services and relational data
Tim,
This is an off-topic question. When is it going to be possible to develop Silverlight applications for the iPhone (using VS.NET)?
1/12/2010 7:52 PM | # re: RIA Services and relational data
Tom -- as soon as iPhone platform enables it we can begin to explore the tooling and necessary changes we would need to make.
1/15/2010 10:19 PM | # re: RIA Services and relational data
Thanks you, great article. I've been struggling for two months trying to do this with DataGrid. While I still can't get associated data to show in a DataGrid, the TextBox, ListBox, and ComboBox's work fine.
1/20/2010 12:35 AM | # re: RIA Services and relational data
Hi Tim
An associated question - is it possible to persist data in an out of browser app which is disconnected?
Gravatar
1/22/2010 8:08 AM | # re: RIA Services and relational data
Hey Tim,
love your posts.
Is there a way to have my own framework(DTO, DALC, ...) while using RIA?! My assumption is that I have to use entity models to be able to use RIA.
Thanks,
1/26/2010 6:01 PM | # re: RIA Services and relational data
Lets say I have a query which returns data from across two tables which have a Foreign/Primary Key relationship

i.e

ServerGroup

ServerGroupID

ServerGroupName



Server

ServerGroupID

Hostname


And a query in my domain service like this

public IQueryable<ServerFarm> GetServerFarmbyID(in ServerGroupID)
{
return this.ObjectContext.ServerGroup.Include("Server").Where(c => c.ServerGroupID.Equals(ServerGroupID));
}

So I should get all of the "Server" table entries as well as the "ServerGroup" that matches the "ServerGroupID" passed.

Can I then in a Dataform datafield bind the fields individually of the "Server"?

So something like


<dataForm:DataField Label="Hostname">
<TextBox Text="{Binding Hostname, Mode=TwoWay}" />
</dataForm:DataField>

The reason I want to do this, is because if I query the "Server" table on its own, then when I add a New "Server" entitiy through the dataform the "ServerGroupID" isn't set. I was hoping if I do it this way, there would be some smarts which would automatically fill in the "ServerGroupID" for me?
1/28/2010 2:53 AM | # re: RIA Services and relational data
How about LinqToSql ? I can't find the Include function !!!
2/2/2010 2:38 PM | # re: RIA Services and relational data
Tim:

Does this work with SL3 Ria WCF Services?
I am trying it with two DataGrids, rather than list boxes as you have and the child grid is not displaying any data.
2/2/2010 5:15 PM | # re: RIA Services and relational data
Stacy -- it should. Make sure you put the attributes correctly and modify your methods to return the query.
2/15/2010 11:25 AM | # re: RIA Services and relational data
Hi Tim,
I can get this to work with a single foreign key relationship, but how do I do this for more than one table, both foreign keyed to the primary table?
-Erin
2/18/2010 3:05 PM | # re: RIA Services and relational data
Hi Tim,
2/18/2010 3:10 PM | # re: RIA Services and relational data
What about using Stored Procedures in your example? can u help me with this??? could be by email.. thanks man!

Great Post
2/19/2010 8:21 AM | # re: RIA Services and relational data
I've seen the questions about the many to many relationships, and I'm wondering if there is a solution for this. I've been searching as of yet cannot find anything.
3/3/2010 8:00 AM | # re: RIA Services and relational data
Thanks Tim for a very useful post.

For all of you that has been looking for an option to include more than one foreign key, I cannot find any support for this either.

That's the bad news.....but AlexJ at Meta-Me has produced some very useful Include extensions that makes it possible to do both sub-includes and multiple includes.

Have a look here: blogs.msdn.com/.../...ment-include-strategies.aspx

Have tried it - works like a charm...

3/3/2010 9:09 AM | # re: RIA Services and relational data
Similar to what Cliff Eby describes in his post Jan 15, I cannot get this to work with DataGrid using AutoGenerateColumns="False".

This one won't work, even if the datacontext contains information for "Albums.ID":

<data:DataGridTextColumn x:Name="IDColumn" Binding="{Binding Path=Albums.ID}" Header="ID" />

If I use AutoGenerateColumns="True", and bind the ItemSource to the included Entity (Albums), it works like a charm.

If anyone has found the solution to this, please feel free to post some sample code.
3/4/2010 1:18 PM | # re: RIA Services and relational data
Solved by adding ItemsSource = "{Binding Albums}" to the DataGrid, and changing the columns binding from Binding="{Binding Path=Album.ID}" to Binding="{Binding Path=ID}"
4/26/2010 1:21 AM | # re: RIA Services and relational data
Hi Tim

First, thank you for all your helping posts. I read them all. Could you help solving me the following problem?

I wish to convert SL3 WCF RIA application into SL4. At the momemnt I am trying to get data into a datagrid from the third relational tabel which is related to the second. I have SL4 and using WCF RIA.

In SL3 I have included the third table into service like:
....Include("MemberDetail.CommonData")

and included columns in service metadata like:
[MemberDetail("FirstName","FirstName")]
[MemberDetail.CommonData("Town","Town")]
public MemberDetail MemberDetail {get; set;}

In dataGrid cloumnc binding I had:
....Path=FirstName....
....Path=Town....

I works OK in SL4 too, however I wish to use more elegant way. When I put the same "include" into service and the following:

[Include]
public MemberDetail MemberDetail {get; set;}

into service metadata, I can get all the columns from the relational table into dataGrid with bindings like
....Path=MemberDetail.FirstName....

but not for the third table, with the following:
....Path=MemberDetail.Common.Town....

I suppose I have to add some "Include" into service metadata, but I do not know how.




4/28/2010 9:52 AM | # re: RIA Services and relational data
Solved, by adding [Include] in a second entity matadata class too
Gravatar
5/27/2010 7:50 AM | # re: RIA Services and relational data
Multiple foreign keys are actually pretty easy.
In your DomainService.metadata.cs, decorate your sub-entities with the [Include].

Then in your DomainService.cs, in the IQueryable of your main entity, add the appropriate includes for your sub entities.

return this.ObjectContext.main_entity.Include("foreign_key_entity1").Include("foreign_key_entity2").Include("foreign_key_entity3").Include("foreign_key_entity4")

You can even do this on lower level entities
return this.ObjectContext.main_entity.Include("sub_entity.foreign_key1.foreign_key1a").Include("sub_entity.foreign_key2.foreign_key2a").Include("sub_entity.foreign_key3.foreign_key3a")

Then, compile, go to the datasources in the designer, expand your main entity and drag the related entities (that are included as part of your main entity) to your work surface.

Write your submit changes button.
private void saveButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
routeMasterStepDomainDataSource.SubmitChanges();
}

And Bam! All the child tables tables are updated automagically.

Now I just need to know how to turn a foreign key into a combobox in Silverlight 4 using visual studio 2010.
6/19/2010 9:09 AM | # re: RIA Services and relational data
I am getting an error when adding the [Include] attribute to the domain service class

Error 1 Attribute 'Include' is not valid on this declaration type. It is only valid on 'property, indexer, field' declarations.
6/19/2010 9:13 AM | # re: RIA Services and relational data
Ooops - my mistake: should have been in the metadata class :-)
7/27/2010 11:16 AM | # re: RIA Services and relational data
Tim,

Will the [Include()] attribute work in a separate partial class file outside of the autogenerated RIA Services metadata file?

If you regenerate the domain service class, won't the metadata file erase any custom [Include()] attributes you decorate to any properties or entity collections?

thanks,
John
8/4/2010 7:20 PM | # re: RIA Services and relational data
Disculpa mi Ingles

En cual archivo de meta dato tengo que poner [Include]

Gracias
8/28/2010 10:27 AM | # re: RIA Services and relational data
Tim,

Have used this technique before in SL3 etc. BUT when we have to rebuild the DomainService - ie add new data table into the model etc. - these changes are lost.

I was hoping that Entity Framework 4 would fix this issue - but you still seem to be recommending that we modify auto-generated code :-((. Sureky that should be a NO-NO!!.

Same goes for adding new methods - the examples always show adding this to the auto-generated domainservice code files - why ??.

Surely there must be a cleaner way to do this by now ?? - am I missing something here ??.

I have worked around this in the past by creating a GSDomainService as a wrapper - which inherits the auto-generated class, then add my methods into my own file. Its KLUNKY but it works for new methods but NOT for the [Include] attribute :-(.

NB Your articles and video's are always helpful and are most welcome ;-).

Thanks

Graham
9/2/2010 2:40 PM | # re: RIA Services and relational data
This is probably a bad idea with large data. but i was having an issue getting entities of entities. i was able to get it to return all my data by making my query look like this:
<Query(IsDefault:=True)> _
Public Function GetProjects() As IQueryable(Of Project)
Dim RE As New ResourceEntities
Return RE.Projects.AsQueryable
End Function

i have all my include tags and everything, not sure if i still needed them to get that.
9/7/2010 9:57 PM | # re: RIA Services and relational data
Thank you very much! this is what I was looking for
9/18/2010 7:57 AM | # re: RIA Services and relational data
Hi Tim,

How about doing the same but with Insert.
I did what you suggest and it works like magic retrieving the main table and its join table in one transaction.
The main table ID is generated by the database, and I need to use it also for inserting into to the join table in one transaction.
I tried it and it's not working for me.
Could you add a section to this article for the insert operation?
I hope you'll answer this :)
Thanks

Dror

10/12/2010 8:51 AM | # re: RIA Services and relational data
Tim,

This works great, Now if I have a language table say related to artists in a 1 to many relation.
How do I retrieve data from language table inclusive of albums inclusive of artists.

I understand how to retrieve from language to albums using "Include" Command but can you please tell how to retrieve 2 levels deeper tables?

In ADO .NET I can do like language.Include("Albums\Artists").
10/25/2010 7:33 AM | # re: RIA Services and relational data
Since it's generated code, this approach is an 'utterfail' - an include query should be picked up by RIA services without jumping through these hoops
10/25/2010 2:58 PM | # re: RIA Services and relational data
so if you want to include related tables, you have to get everything in each table?

return ObjectContext.Artist.Where(a=> a.ArtistID == _artistID).Include("Albums")

does not seem to work... or are there more metadata attributes I need, besides include? Something about the relationship? I already have keys defined in the database though
12/17/2010 10:04 AM | # re: RIA Services and relational data
What happen if an Album has many artist?
I think you can still feel guilty because I can't find a good exemple of a real world application, neither yours. When was the last time you've developed a master-detail system without a link or junction table.
In real world you have Albums (AlbumId,Title) with many Artist(ArtistId,Name) and, this is the question, a junction table ArtistAlbum (ArtistId, AlbumId) the connect both.
Will you be so kind to give us a Master-Detail with Junction Talbe example?

Thanks,
1/14/2011 6:53 AM | # re: RIA Services and relational data
Thank you so much for this great post.
I was so bored to see only basic examples with only one table...

2/6/2011 3:23 PM | # re: RIA Services and relational data
Thank you so much. This saves my day.

Actually there are samples for RIA with multiple tables. But those samples do not mention [Include] for the metadata. Microsoft should do a better job documenting this. This is why samples from Microsoft always work but it never works when you want to create one youself.

Calvin Chen

3/12/2011 6:10 AM | # re: RIA Services and relational data
Why doesn't Microsoft announce 70-271 test that they are 70-620 test NOT supporting Visual Basic anymore. Seems like every sample and any code 70-653 test written with Silverlight is in C# and us VB programmers have to beg to get the code also written in VB.
I think there is still a hugh percentage of coders using VB,70-630 test and we should not be slighted like this by anyone.
4/22/2011 2:12 AM | # re: RIA Services and relational data
Nice work Tim!
I'm wondering is it possible to add [include] attribute at the top of Artist property of Album class in .metedata file, like this:

class Album
{
.
.
[include]
public Artist Artist{ get; set; }
.
.
}
So we can reach the ArtistName in Albums datagrid!
Is there a way?
5/16/2011 6:15 AM | # re: RIA Services and relational data
Hi,

I am starter with SL, i am trying to figure out the meaning of the code, can some one please tell me that the menaing what i s said in this line of the code, thanks

Roly


ChinookContext ctx = new ChinookContext();

9/16/2011 6:10 AM | # re: RIA Services and relational data
The sourcecode in the download doesn't seem to work (anymore).

Using VS2010 with latest RIA and it keeps returning erros about missing references, complains about [Include] , etc...

:(

11/8/2011 5:35 AM | # re: RIA Services and relational data
Hi Tim,

Can I use only the Count property?

I want to show some data for an album, like the latest album name for an artist.
5/13/2012 12:57 AM | # re: RIA Services and relational data
Hi Tim,
Thanks for all the info on your site, i have been working with getting relational data from the database. I am having an issue that when i change the child entities, the parent entity property HasPropertyChnages= False whereas HasChildChanges=True. Due to this my dataform does not activate the "Submit" button.

Can you help me with how one can work with RIA and Relational Data and DataForm.

Thanks in advance

Ashwin

 
Please add 1 and 2 and type the answer here:

All postings/content on this blog are provided "AS IS" with no warranties, and confer no rights. All entries in this blog are my opinion and don't necessarily reflect the opinion of my employer or sponsors. The content on this site is licensed under a Creative Commons Attribution By license.