Saturday, August 14, 2010

Project Silverlight RIA Methods to Access All Data in Context

Silverlight Data and Controls Series

Background

I have a little trick I have been doing lately with my custom RIA Services to be able to access all the objects that get returned the Context on the Silverlight Application Side. The Context object, which is generated for each RIA Domain Service you have, is a bunch of hash table like collection of objects when you look underneath the hood.

The code generation I just mentioned is commonly referred to as “code projection”. What happens is when you compile your Silverlight application that is using RIA Services you will see a hidden folder called Generated_Code. In there you will find several classes have been generated. First is the context class which is generated for each RIA Domain Service you have. This class will have all the RIA methods you have defined. Second you will see tons of classes in there for each object type that is returned out of the RIA Services. In my case, I have tons of custom DTOs being returned from the RIA methods, so there will be a corresponding type returned.

Here is an example of a RIA method you may create:

[EnableClientAccess()]
public class SimpleService : DomainService
{
public List<ClassA> GetClassAs()
{
return DataService.GetClassAs();
}
}

You will notice that is an extremely simple method and it is returning a collection of DTOs from some data tier. In this example, the ClassA DTO has two encapsulated DTO lists within it (i.e. a one-to-many relationship of data). Here are the definitions to the three DTOs and their relationships to each other:

public class ClassA
{
[System.ComponentModel.DataAnnotations.Key]
public int ID { get; set; }
public string ValueA { get; set; }
private List<ClassB> _classBs = new List<ClassB>();

[Include]
[Association("ClassA_ClassB", "ID", "ClassAID")]
public List<ClassB> ClassBs {
get {
return _classBs;
}
}
}

public class ClassB
{
[System.ComponentModel.DataAnnotations.Key]
public int ID { get; set; }
public int ClassAID { get; set; }
public string ValueB { get; set; }
private List<ClassC> _classCs = new List<ClassC>();

[Include]
[Association("ClassB_ClassC", "ID", "ClassBID")]
public List<ClassC> ClassCs
{
get
{
return _classCs;
}
}
}

public class ClassC
{
[System.ComponentModel.DataAnnotations.Key]
public int ID { get; set; }
public int ClassBID { get; set; }
public string ValueC { get; set; }
}

Then in your Silverlight application, you will call the RIA Service to get your data like the following. As you can see I can access all ClassAs in a LINQ query but I cannot access all of the ClassCs in this manner. In the code below, you will see there is a compile error.

private void GetData() {
_simpleContext = new SimpleContext();
_simpleContext.Load(_simpleContext.GetClassAsQuery(), GetClassAsLoaded, null);
}

private void GetClassAsLoaded(LoadOperation<ClassA> lo)
{
if (lo.HasError)
{
lo.MarkErrorAsHandled();
//do something
}
else
{
//Compiles
var foos = from A in _simpleContext.ClassAs
where A.ValueA == "Foo"
select A;

//Will NOT COMPILE
var bars = from C in _simpleContext.ClassCs
where C.ValueC == "Bars"
select C;
}
}

This was pretty annoying because if I put a breakpoint on context and then do a quick watch I was easily able to view the data. It was even more annoying because RIA enforces that each object instance be unique within the context (SimpleContext in my example). So why can I not just be able to access the list of ClassCs directly to perform an operation. The only solution you would think is to loop over ClassA, then ClassB, and then ClassC.

Solution

The solution I came up with is rather simple. On the RIA service add the following method.

[EnableClientAccess()]
public class SimpleService : DomainService
{
public List<ClassA> GetClassAs()
{
return DataService.GetClassAs();
}

public List<ClassB> GetClassBs()
{
return new List<ClassB>();
}

public List<ClassC> GetClassCs()
{
return new List<ClassC>();
}
}

What this will do is project properties on the context class definition so I can directly access the collection of encapsulated objects. Notice the two new methods have no implementation, nor is any needed either. Now the code above will compile. This example is not really the compelling however when you have a very large custom RIA Service layer you are writing for a big Silverlight application, you will find this very useful.

No comments: