(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

WCF RIA Services Part 5 - Metadata and Shared Classes

(9 votes)
Brian Noyes
>
Brian Noyes
Joined Jun 10, 2010
Articles:   19
Comments:   116
More Articles
41 comments   /   posted on Aug 17, 2010
Categories:   Data Access , Line-of-Business

This article is Part 5 of the series WCF RIA Services.

Introduction

In this article, I am going to quickly cover the metadata and shared code facilities of WCF RIA Services. In a nutshell, metadata classes allow you to add functionality in the form of attributes to the code generated entities that you are using in your services and on the client. You can add attributes that causes validation to happen, which I will go into more detail on in the next article, as well as attributes that affect the way entities are related and transmitted across the wire by RIA Services. With shared code, you can define chunks of logic or partial class extensions for your entities that are defined once on the server side, but get code generated on the client side as well so that the same logic can be used in both places.

For this article and moving forward with the rest of the series, I am going to depart from the step-by-step instruction format for the articles. Because I am building and adding functionality to the application with each article to show some of the more sophisticated capabilities of RIA Services, I cannot easily lead you through writing all the code without writing an unnecessarily long tome for each article. So I’m going to switch to a more descriptive format where I will simply highlight the appropriate sections of the code for the concepts discussed in the sections of the article.

You can find the completed code for this article here. In this articles version of the application, I have added the ability to display and add time entries associated with a task.

Metadata Classes

When working with WCF RIA Services, your client side entities are code generated at compile time as discussed in earlier articles in the series. At a simplistic level, WCF RIA Services tools reflect on the compiled entity types at compile time and generate entities with the same set of properties on the client side. However, any methods or behavior embedded in the property definitions that are part of the entity type on the server side are not carried over to the client side. If you are working with POCO entities, many attributes on your properties will also be regenerated on the client side. Those attributes can include validation attributes from the data annotations namespace, or they can include certain attributes that are used by RIA Services to influence how it tracks changes on related objects and which related objects it transfers across the wire.

The challenge is that if you are using a technology like Entity Framework, the entity classes themselves are generated code that you should not be directly modifying because it will be regenerated if you make modifications to the model in the designer. So you can’t really add attributes to the entity definitions themselves. You might start to think - “well, I can just use partial classes to add what I want”. But you would be wrong. While it is easy to add methods and new properties to a class via partial classes, you cannot really modify existing properties via a partial class extension.

Along come metadata classes to the rescue. A metadata class, or “buddy class” as it is called, is a way to solve this conundrum. A metadata class is a simple class that defines the same or a subset of the same properties defined by one of your entity classes, but those properties have no implementation and won’t actually be called at all. They are just a place where you can add attributes, and those attributes will be added to the corresponding properties in the code generated client entities. Depending on what the attributes are, they will also be used by RIA Services on the server side.

Let me show an example. In the sample application for this article, I have added the ability to display and add child TimeEntry objects to a Task object. In the Entity Data Model for the sample, the Task type has a 0 to many relationship with a set of child TimeEntry types. In the object model, this looks like this:

8-11-2010 8-28-57 AM

As mentioned, these entities are defined through code generation by Entity Framework, so I don’t have the opportunity to modify them.

If I want to make the Description property on a TimeEntry a required field, I can use the [Required] attribute from the System.ComponentModel.DataAnnotations namespace. If I do that, I would get automatic validation of that in the UI so I end up with a UI that looks like this:

8-11-2010 8-31-38 AM

I can actually get this pretty much “for free” with RIA Services and Silverlight by just adding a [Required] attribute to the Description property on the TimeEntry type. However, I can’t put it in the entity code generated by Entity Framework, so I need a metadata “buddy” class to add it to the entity.

My metadata class looks like this:

 [MetadataTypeAttribute(typeof(TimeEntry.TimeEntryMetadata))]
 public partial class TimeEntry
 {
     internal sealed class TimeEntryMetadata
     {
         // Metadata classes are not meant to be instantiated.
         private TimeEntryMetadata()
         {
         }
         [Required]
         public string Description { get; set; }
     }
 }

The convention is to define a nested class inside a partial class extension for your entity type on the server side. You link the metadata class to the entity type through the MetadataType attribute. The metadata class can redefine any of the properties exposed by the entity type and add attributes to them. You do not have to redefine all of them, only those you want to add an attribute to. So in this case I just add the [Required] attribute to the Description property. These metadata classes can be generated for you when you run the initial Add Domain Service Class template. The “Generate associated classes for metadata” checkbox at the bottom of the Add Domain Service dialog will create a metadata class for each entity managed by your domain service and will redefine all of the properties in those entities within the metadata class so that you can easily just drop in an add attributes as appropriate.

The generated client entities will then contain these attributes:

 public sealed partial class TimeEntry : Entity
 {
 ...   
     [Required()]
     public string Description
     { }

Other attributes that are important in the metadata class have to do with entity relations. When you define retrieval methods in your domain service for an enitity type such as a Task, by default WCF RIA Services will not send related entities to the client. So even though the Task type has a property for a collection of child TimeEntries, even if my domain service loads all the child entities (with the Include() method in Enitity Framework), those child entities will not be sent to the client. To get them sent, I first have to ensure that they get loaded from the data layer:

 public IQueryable<Task> GetTasks()
 {
     return this.ObjectContext.Tasks.Include("TimeEntries").OrderBy(t=> t.StartDate);
 }

And second I need to add an [Include] attribute on the corresponding property in the entity metadata class:

 [MetadataTypeAttribute(typeof(Task.TaskMetadata))]
 public partial class Task : INotifyPropertyChanged
 {
     internal sealed class TaskMetadata
     {
         // Metadata classes are not meant to be instantiated.
         private TaskMetadata()
         {
         }
         [Include]
         public EntityCollection<TimeEntry> TimeEntries { get; set; }
     }
 }

Now the time entries get sent to the client side and I can display them (after adding some of course):

8-11-2010 8-21-15 AM 

If you want to manage child entities as wholly owned child objects of the parent, you can also use the [Composition] attribute. Putting that on a property makes it so a change to a child object causes the parent object to be marked as modified. Then when you SubmitChanges on the client side both the parent and the changed child entities are sent along. You do have to do a little more work in your parent entity Update method because you have to handle the inserts, updates, and deletes of your children as well as those for the parent entity type.

Shared Code

Another facility that comes in handy is the ability to define a chunk of code on the server side that can be used there, but that will also be available (through code generation) on the client side as well. All you need to do to leverage this is to put the code in a file with a *.shared.cs (or *.shared.vb) naming convention in the server project. The sample code for this article includes a file named Task.shared.cs containing a partial class extension for the Task entity type that adds a computed property:

 public partial class Task
 {
     public TimeSpan TotalTime
     {
         get
         {
             TimeSpan total = new TimeSpan();
             if (TimeEntries != null)
             {
                 TimeEntries.ToList().ForEach(delegate(TimeEntry te)
                     {
                         total += (te.EndTime - te.StartTime);
                     });
  
             }
             return total;
  
         }
     }
 }

This property could be used to support the business logic on the server side, and is used for display on the client side as you can see from the screenshot shown earlier.

Another common use of shared code is for custom validation rules that get tied in to a property through a [CustomValidation] attribute. I’ll show an example of that next article when I go into a little more depth on validation.

Summary

Metadata allows you to add functionality to your server and client side entities by decorating properties in a metadata class with attributes that you want to affect the behavior of your entities. A metadata class is a class tied in to the entity type through the [MetadataType] attribute. You can tie in validation attributes, as well as attributes such as the [Include] and [Composition] attributes that influence the way WCF RIA Services handles child entities. Shared code allows you to share code files between the client and server side without needing to copy and maintain two versions. All you need to do for that is name your code files with a .shared.cs (or .vb) naming convention.

Next article I’ll dig in to validation a bit more.

You can download the sample code for this article here.

About the Author

Brian Noyes is Chief Architect of IDesign, a Microsoft Regional Director, and Connected System MVP. He is a frequent top rated speaker at conferences worldwide including Microsoft TechEd, DevConnections, DevTeach, and others. He is the author of Developing Applications with Windows Workflow Foundation, Smart Client Deployment with ClickOnce, and Data Binding in Windows Forms 2.0. Brian got started programming as a hobby while flying F-14 Tomcats in the U.S. Navy, later turning his passion for code into his current career. You can contact Brian through his blog at http://briannoyes.net/ or on twitter @briannoyes.


Subscribe

Comments

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Veeru on Aug 20, 2010 13:08
    simple and helpful
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Aga on Aug 24, 2010 20:47
    can't wait for your next post:)
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Minal on Aug 27, 2010 13:37
    Nice Article
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Anand Subramanian on Aug 31, 2010 00:14
    You rock
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Ray on Aug 31, 2010 23:17
    How can I add metadata when I don't know what the concrete type will be? I have an interface for my entity and do not know how it will be implemented.
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on Sep 01, 2010 00:03
    I don't believe RIA Services will support interface-based entity types. It just works with concrete types that are part of your entity model.
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Dick in London on Sep 11, 2010 17:20
    FYI the intro text "This article is Part 4 of the series WCF RIA Services" should read "This article is Part 5 of the series WCF RIA Services"
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Bruce Do on Sep 17, 2010 23:25
    i don't understand much about shared code between Server and Client Side. As I know, all code will be compiled to dll as well.
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on Sep 18, 2010 07:50
    Bruce: The shared code files declared on the server side are code generated (copied) to the client side project at compile time and do get compiled into both the server assembly and the client assembly.
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Geert on Sep 20, 2010 15:51

    when I add some code like 


    public string ShortDescription
            {
                get
                {
                    if ((!string.IsNullOrEmpty(Description)) && (Description.Length > 200))
                        return Description.Substring(0, 200);
     
                    return Description;
                }
            }


    where Description is a property of my DTO class defined as 


    public string Description { get; set; }


    into a shared.cs, the content of the property ShortDescription is sent over the wire to the client. Is there a way to execute the ShortDescription logic on the client only?


  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on Sep 20, 2010 17:30

    Sure, only declare the partial class for your DTO in the client side project. You can still enhance the client side class yourself through partial class extensions in the client project in addition to what gets code generated and shared with the server side.

    Another example where you have to do this is if you want to have property change events fire, you override the partial methods defined in the code generated client class for each property changing.

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Geert on Sep 21, 2010 15:24

    yup that's done the trick, saved me 25% in data transfer :D

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Geert on Sep 21, 2010 17:19

    yup that's done the trick, saved me 25% in data transfer :D

  • aarti291184

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by aarti291184 on Dec 01, 2010 07:59

    Hi Brian,

    When I tried this post on yesterday ,I did not get data for Tasks and Time Entry entities by using metadata classes 

    Can you plz help me

    what was the issue?

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on Dec 02, 2010 00:03

    aarti291184:

    Not clear what you mean with respect to "by using metadata classes". I've just confirmed on a clean machine that downloading the code, running the SQL script against a default SQL Server instance, and running works fine. If you ran the SQL script against a non-default instance of SQL (i.e. Express), you will have to modify the connection string in the web.config to reflect that to get the code to run. There are no time entries in the DB with the default data, just four tasks. But you should be able to add time entries to any of the tasks.

  • aarti291184

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by aarti291184 on Dec 02, 2010 07:30

    Hi Brian,

    My mandatory message is not coming with the description textbox in the UI even if i added required attribute with respect to Description field in the TimeEntryMetadata

    what else i need to add it for coming validation message?

    Plz guide me

    I am new in the Silverlight

    And also give me guidance for how to learn Silverlight step by step

    Thanks in advance

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on Dec 02, 2010 11:52

    Are you using the code download and modifying it or trying to code it up yourself? The download code should display a validation error (red box) on the Description field with a tooltip if you hover over the little red triangle in the upper right corner of that control. If that is not showing up with the unmodified code from the download, not sure why that would be.

    If you are coding it up yourself, you also need the binding on the textbox to enable validation for INotifyDataErrorInfo and exceptions:

    Text="{Binding Description, Mode=TwoWay, ValidatesOnNotifyDataErrors=True, ValidatesOnExceptions=true, NotifyOnValidationError=True}"
    For learning Silverlight in general, I'd strongly recommend the following two books:

    Silverlight 4 Unleashed, Laurent Bunion

    Silverlight 4 in Action, Pete Brown

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by aarti291184 on Dec 02, 2010 19:16

    Hi Brian,

    I already have added all the attributes as per you mentioned in your reply but still its not coming.

    Thanks for the books

    Could you also recommend some video's links for the Silverlight

    whats the role of prism in Silverlight?



  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on Dec 03, 2010 00:44

    Watch all the episodes of Silverlight TV. :)

    Prism is a framework for building loosely coupled, composite applications. It has features for building up your app out of loosely coupled modules, dynamically composing the UI out of loosely coupled UI parts, and having loosely coupled communications between those modules and their classes with commands and events. You can find out more by reading the book-style documentation that comes with the download at prism.codeplex.com, and it will also be released in Print form through O'Reilly in Feb timeframe, titled Developers Guide to Microsoft Prism 4 (I co-authored).

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by sam on Dec 19, 2010 21:08
    How can Pass Id from one page to other using MVVM
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on Dec 20, 2010 02:27

    Sam,

    Not sure I know what you mean. If you are really meaning page to page, you would leave the Silverlight application so it wouldn't apply. If you mean navigating from one logical page or screen to another within the Silverlight application, MVVM doesn't really address that at all. MVVM is all about how a view model supports a single view. It doesn't address how you navigate from view to view. For a couple ways to tackle that challenge, you can either use the Silverlight Navigation Framework or use Prism 4 navigation. The former is part of Silverlight, pick up a good book on Silverlight such as Silverlight 4 Unleashed or Silverlight 4 in Action, and the latter you can download and read about in the docs at prism.codeplex.com.

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by terry.bayles@att.net on Dec 27, 2010 22:25
    do you have the instuction book I can print out since I lost mine to the Ria studiotrack 4 model no.R504 I cannot get it to work properly. I would appriciate it or if you know where I Can find it. so I may print it ou. thank you. terry
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by nikunj on Dec 30, 2010 06:41
    Really helpful post..
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Jason Wang on Apr 20, 2011 09:05

    hi mentor,

     the namespace looks like System.Windows.Interactivity that isn't find out from your sample. Could you give me some guildlines to solve this issue. Thanks a lot!

     

     

     

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on Apr 20, 2011 13:56

    Hi Jason, (and for the benefit of anyone else with the same question)

    System.Windows.Interactivity and Microsoft.Expression.Interaction libraries are part of the Expression Blend SDK, which is a freely downloadable and distributable library even if you don't have Blend. It includes a number of built in behaviors from Blend as well as the base classes to build your own and the attached properties to attach those behaviors to your elements in XAML.

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Femi Oyekan on May 02, 2011 03:21

    Very concise, extremely helpful.  Thanks.  

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Myles Johnson on May 19, 2011 16:57
    Great article.  Thanks.
  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Subt13 on May 24, 2011 19:26

    Great article. How does one generate the .meta.cs file without using the checkbox?  I have an issue where none of my entities show up in the dialog.

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on May 24, 2011 19:31

    Your entities will only show up if a) they are Entity Framework generated entities as part of a Entity Data Model, and b) that model is either in your domain service project or a referenced assembly. You do have to build before they will show up too.

    There is no magic to the wizard with respect to the metadata class, you can create the same setup with Add New Class, define a partial type for your entity type, put the MetadataType attribute on it to point to the metadata class, and then define the properties you want to add attributes to on the metadata class.

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Teun on May 30, 2011 14:56

    Hi Brian,

    is it possible to control in which projects shared classes are generated?

    Because I have one project for the data classes and it is used in several ria service projects. But in some services not all classes are used/generated. This means that in some services I have the shared validation classes are generated and not the data class so I get a compile errors in the generated code (for example:  var user = context.ObjectInstance as User).

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on May 30, 2011 14:59

    Hi Teun,

    You can control which server project a client points to through the WCF RIA Services Link setting in the Silverlight project. But you always have to have a 1:1 relationship there. See part 9 about structuring projects to get better idea of how all that works.

  • -_-

    RE: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Brian Noyes on May 30, 2011 15:02

    Oh, and I should also mention that with SP1 (which is installed with VS 2010 SP1), there is an improvement in the way the classes get generated from a single domain service project so that multiple domain services can expose the same entity type.

    But if you have links to the server projects in a client project, that project should have generated types for the domain context, entity types, and any .shared. classes.

  • Oged

    Re: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Oged on Aug 23, 2011 10:43
    good
  • Benoit73

    Re: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by Benoit73 on Oct 14, 2011 01:24
    The OKbutton event was not associated and the delete time entries does not delete the selected one. There is nothing in the method.
  • brian.noyes

    Re: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by brian.noyes on 14:40
    Benoit73, that is fixed in the source code for the next article I believe. Demo-ware, not intended to have every function working.
  • HoudiniSutherland

    Re: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by HoudiniSutherland on Jan 29, 2012 21:03

    Nice, but I am having a bit of trouble here. I am not using EF, I am using LINQ2SQL. Not there is no include method as you explained.

     public IQueryable<Task> GetTasks()
     {
         return this.ObjectContext.Tasks.Include("TimeEntries").OrderBy(t=> t.StartDate);
     }
     I would believe Linq2Sql uses a LoadWith(), but that is not there either. Any help?
  • HoudiniSutherland

    Re: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by HoudiniSutherland on Jan 29, 2012 21:26

    Nice, but I am having a bit of trouble here. I am not using EF, I am using LINQ2SQL. Not there is no include method as you explained.

     public IQueryable<Task> GetTasks()
     {
         return this.ObjectContext.Tasks.Include("TimeEntries").OrderBy(t=> t.StartDate);
     }
     I would believe Linq2Sql uses a LoadWith(), but that is not there either. Any help?
  • brian.noyes

    Re: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by brian.noyes on Jan 29, 2012 23:10

    Its been a while since I have done L2S - thought it eager loaded by default. But unfortunately can't coach you on how to get data loaded with L2S here since I don't use it. Bottom line, if you can confirm the data access pattern with L2S that would get the data loaded on the server if you were to loop over the IQueryable<T> then it will work from there like EF. The other thing I am not sure of is if the L2S entities include the right attributes (i.e. Association attribute) to make sure things get transfered properly.

  • HoudiniSutherland

    Re: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by HoudiniSutherland on Jan 29, 2012 23:13

    Nice, but I am having a bit of trouble here. I am not using EF, I am using LINQ2SQL. Not there is no include method as you explained.

     public IQueryable<Task> GetTasks()
     {
         return this.ObjectContext.Tasks.Include("TimeEntries").OrderBy(t=> t.StartDate);
     }
     I would believe Linq2Sql uses a LoadWith(), but that is not there either. Any help?
  • JavidBahramzy

    Re: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by JavidBahramzy on Aug 21, 2012 17:49

    Thanks for these nice series on WCF RIA Services.

    When adding a Time Entry, it's always added to the first Task in grid even thought I select another Task and try to add a Time Entry, it's added to the first one. Any idea how I can fix this problem?

    Thanks for your time. 

  • brian.noyes

    Re: WCF RIA Services Part 5 - Metadata and Shared Classes


    posted by brian.noyes on Sep 02, 2012 15:51

    I can't repro what you are describing with the sample code. When you select a Task and add a time entry, it is added to the selected task, and you can inspect the handling code in the view model and see that is the case. Selecting a different task and the time entry is no longer shown because it is not part of that task, go back to the one you added it to and it shows up. Its working as designed for me.

Add Comment

Login to comment:
  *      *       

From this series