Experiments with RIA Services

Unfortunately I have been so busy as of late I have not had the time I would like to dedicate to exploring the .NET RIA services.  I have been quite impressed from what I have seen on video and heard from colleagues (one colleague is already planning to use it in an enterprise project he is writing).

This weekend I decided to sit down and at least understand the mechanics of transmitting data to and from the server and how to extend existing classes to increase functionality.  What I found is that RIA Services have a bit of a learning curve while you get your head around the model, but once you do, it works very nicely.  My plan was to demonstrate basic binding with an entity generated by the Entity Framework, then move on to include a single related entity, then finally a Many to Many relationships.  To start I brought in 4 classes from my AnimeManager database.  As expected EF picked up the Many-to-Many and hide the relational table, leaving me with the following:image

Let me state that THIS DOES NOT WORK!!  The RIA services DO NOT support a many to many relationship defined this way.  It took me a bit of scouring but I finally came across this blog entry which explains things: Creating Apps with RIA Services (part 3)

Essentially the key is HOW you add the entities, because you WANT the relational tables.  So in this case I deleted the existing EDMX and added this as follows:

  1. Added Studios, Series, SeriesGenres
  2. Allowed the generation of the models
  3. Used Update Model from Database to add the Genres table
  4. Removed the generated relationship between Genres and Series
  5. Recreated this relationship manually by relating Genres to SeriesGenres via this dialogimage
  6. IMPORTANT!!!! THE NAME OF THE RELATIONSHIP MUST MATCH THE FOREIGN KEY NAME FROM THE DATABASE
  7. After adding this you will set get an error about Association type, use Mapping Details to ensure that this relationships maps to your Many type (SeriesGenres in this case)
  8. Build, everything should go through without fail, once it does you can proceed to the next portion, creating the domain service.

RIA Services creates Services that handle the interaction between the underlying WCF web services and the data extracted via the model layer. To create this access the Add New Item dialog and select Domain Service Classimage

The following dialog is subsequently displayed:image

A service class can support multiple entities, though convention generally dictates that each service represent one entity.  For the sake of this I am going to have all three, you will see why in a moment, it has to do with the many to many relationship.  

The important thing here is that you click “”Generate associated classes for metadata”.

Following this screen VS will update your solution and add two files: SeriesServices.cs and SeriesService.metadata.cs.  It is alright to modify these files as they are only generated once.

By using the SilverLight Business Application template, there is something else going on behind the scenes.  VS is looking for these metadata files and is using them to project your classes out to your SilverLight application so they can be used transparently there, with the same namespace I might add.  I find it helpful to include this generated file in the solution, otherwise your code looks like its broken until you compile:image

Notice the file AnimeManager6.Web.g.cs, this is a generated file which contains a representation of the classes in Silverlight.  The one key thing to remember is that your service class (SeriesService in this case) will get generated as a context class instead (SeriesContext in this case).  So the code to bind a datagrid would look something like this:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
     SeriesContext sc = new SeriesContext();
     sc.Load(sc.GetSeriesQuery());
     myDataGrid.ItemsSource = sc.Series;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Note that while this doesn’t appear as such, this is an asynchronous call.  You will find the definition of GetSeriesQuery as GetSeries in your SeriesService method as it is auto generated.  The result is returned, in this case, the Series property of the context, if it where a list of a different type it would likely go there.  This means that attempting to step through this in real time is useless, more often then note you will get an empty set from Series.

As this object is right now, attempting to bind to either our Studio reference or Genres collection would not yield anything. You have to include these data points via the Include when you query the context, and in the case of Studio, mark it with the Include attribute to get VS to project it to the SilverLight class representations.

So lets first take care of the Studio reference, open up your metadata file and add the [Include] attribute to the reference:image

You will note that we are also applying it to the SeriesGenres property, more on this in one second.  With this change in place, update your XAML to look for the Studio navigation property and access its Name property: ie {Binding Studio.Name}

Now this is all good and fine and you may be thinking about extending the projected version of Series in the SilverLight project to include custom properties; don’t, this doesn’t work. I am not entirely sure why, but I will show you how to extend the class and in my mind its a better way anyway.

The best way to add these custom properties is by extending the generated models themselves.  In this case, this is the code I used to create a property for Series which lists out its Genres in a comma delimited list:

public partial class Series
{
    [DataMember]
    public string GenreDisplay
    {
        get
        {
            if (SeriesGenres.Count > 0)
            {
                var sb = new StringBuilder();
                foreach (var genre in SeriesGenres)
                {
                    sb.Append(genre.Genre.Name);
                    sb.Append(", ");
                }

                return sb.ToString().Substring(0, sb.ToString().Length - 2);
            }

            return "No Genres";
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

The key thing here is the [DataMember] attribute, which causes it to be included in the serialization down to the client.  By doing this, it is automatically available to use in the XAML.  Bare in mind that as this being done purely on the server before the object is sent down, it is ONLY reliant on either the EntityContext being open or Include being used when making the query.  It doesn’t not require the objects themselves to use the [Include] attribute.

This gives you flexibility in the design of the class.  It is my opinion, that the generated code is sealed for a reason and modifying the metadata file is likely not a good idea, as this may be something you want to regenerate over time.  However, the other effect of using the [DataMember] attribute will cause VS to project the property out to the SilverLight project automatically.

I hope this helps out, getting this information took me some time and drove me nuts in getting it to work.

Advertisements

One thought on “Experiments with RIA Services

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s