I am going to continue exploring the topic of RIA services and Silverlight. I thought I was going to get into updates in this post, but there are a few topics I would like to cover first. I am going to try to get to updates in the next post.
Today, I am going to explore two more topics. I am going to cover extending RIA Services DomainContext with custom classes and working on including child objects when fetching a parent object using Entity Framework and RIA Services.
First, I am going to extend my DomainContext with a custom class. In my case I have Company Object/Table with a number of columns. In my Silverlight application I would like to have a list of companies that shows only company name. When a user selected one company, I would like to fetch full company object with company contacts child object. If I were to use the method that the wizard built for me – public IQueryable<Companies> GetCompanies(),I would get the full object with all columns. So, I am going to build a custom class that only has company ID and name:
public class ReadOnlyCompany
{
[Key]
public int CompanyId { internal set; get; }
public string CompanyName { internal set; get; }
}
I am going to add this class to RIA Service project on .NET side (SilverlightRIAServicesLibrary.Web project in my case). As you notice I did not have to decorate my class with DataContract attribute or decorate properties with DataMember attribute. We do not actually need to do this, as RIA Services will take care of all that for us. I do however need to use Key attribute or I would get a compile time error. Each object is required to have a key (primary key) property. Next step is to write a method in DomainService (RoldexDomainService in my case) class. Here is what this would look like:
public List<ReadOnlyCompany> GetReadOnlyCompanies()
{
return (from oneCompany in this.ObjectContext.Companies
orderby oneCompany.CompanyName
select new ReadOnlyCompany()
{
CompanyId = oneCompany.CompanyId,
CompanyName = oneCompany.CompanyName
}).ToList<ReadOnlyCompany>();
}
Even though I return List object here, but RIA Services will actually return ReadOnlyObservableCollection<ReadOnlyCompany> on Silverlight side. You have to remember that when you cast return value for RIA Services call to a specific object. Here is what this call would look like on Silverlight side:
private void GetCompaniesList()
{
ShowPleaseWaitMessage();
var query = _context.GetReadOnlyCompaniesQuery();
_context.Load<ReadOnlyCompany>(query, LoadBehavior.RefreshCurrent, (o) =>
{
HidePleaseWaitMessage();
if (o.Error != null)
ErrorHandler.HandleException(o.Error);
else
{
this.Model = o.Entities as ReadOnlyObservableCollection<ReadOnlyCompany>;
}
HidePleaseWaitMessage();
}, null); ;
}
Here I am putting up a please wait window, fire a query and interpret the results. As I mention before, my next step is to get full Company object based on selected ID. Again, I am adding a new method to DomainService class on .NET side:
public Companies GetCompany(int companyID)
{
var returnValue = this.ObjectContext.Companies
.Include("CompanyContacts")
.Include("CompanyContacts.CompanyContactPhones")
.Where(one => one.CompanyId == companyID).FirstOrDefault();
return returnValue;
}
Now, let’s see what this call looks like on Silverlight side. This also demonstrates the use of parameters:
var companyQuery = _context.GetCompanyQuery(_companyID);
_context.Load<Companies>(companyQuery, (o1) =>
{
HidePleaseWaitMessage();
if (o.Error != null)
{
ErrorHandler.HandleException(o.Error);
}
else
{
this.Model = o1.Entities.First() as Companies;
}
}, null);
As you can see, RIA services build us a query that already has the same company ID parameter for us. Pretty cool! However, if I look at return value (Companies object), I will notice that contacts property is actually null even though I did add .Include statement to my custom Entity Framework based method. To get this to work, I have to modify the metadata class that RIA Services generated(RolodexDomainService.metadata.cs in my case). I need to open this class file and look for Companies object. I will find public EntityCollection<CompanyContactPhones> CompanyContactPhones property in it. To get RIA Services to include child collection, I need to add Include attribute here as well:
[Include]
public EntityCollection<CompanyContactPhones> CompanyContactPhones;
I would have to take the same step to include phones collection for each contact. If I run my code again, I will not get Company object with a list of Contacts, each containing a list of Phones.
I will try to cover update in my next post.
Thank you and do not hesitate to ask questions.
Many many thanks Sergey. I finally found this blog after days of trying to work out that which you describe here. I am extremely grateful. Thanks again.
Hi Sergey,
Would you have a small sample application for this?
After adding the Include in both the domain service and the metadata file I still don’t see columns from the included table? By the way, from the following line from your post,
.Include(“CompanyContacts”)
.Include(“CompanyContacts.CompanyContactPhones”)
are CompanyContacts and CompanyContacts.CompanyContactPhone two separate entities?
Thanks.
Correct, contacts and phones are separate entities, where contacts are children of a company, and phone are childrent of a contact.
Sergey
Hi Sergey,
I’m learning RIA Services right now. This is a very helpful feature. Could you please provide a small sample for this?
Thank you very much,
Galina
Hi,
Very nice article. But i got eror while implementing.
//Is my Entity Class
public class ReadOnlyCompany
{
[Key]
public int CompanyId { internal set; get; }
public string CompanyName { internal set; get; }
[Include]
[Association(“Order_Charge_Details_List_Id”, “CompanyId “, “CompanyId “)]
public List FeesCollection { internal get; internal set; }
}
//My Service method
[Query]
public IQueryable GetServiceTest(ReadOnlyCompany readOnlyCo)
{
…
….
}
When i call this service in silverlight codebehind….I am getting
“Parameter ‘chargeMod’ of domain operation entry ‘GetServiceTest’ must be one of the predefined serializable types.”
Please help.
Thanks,
Rajeev
I do not see where you have parameter chargeMod. Probably need to see more of your code to figure this out.
@Galina
Not sure what sample you are looking for…
@Sergey Barskiy
Sorry! i renamed object name from “chargeMod” to “readOnlyCo” but pasted the old error message.
“Parameter ‘readOnlyCo’ of domain operation entry ‘GetServiceTest’ must be one of the predefined serializable types.”
Did you try to change the code to just pass in company ID? I think you are passing a complex object as a parameter instead of just ID. In any case, ID is all you need I would assume.
Could you please specify what errors you are getting?