How to: Handle Relationships
This article is relevant to entity models that utilize the deprecated Visual Studio integration of Telerik Data Access. The current documentation of the Data Access framework is available here.
This topic will give you general guidelines about working with relationships in WCF RIA applications. It will show you how to modify the domain services metadata to support loading of related data.
One-to-Many Relationships
By default, the domain service does not generate any code for associations in your domain classes. Consider the following entities.
The generated Product proxy class won't have any reference to the Category property. To make that property generated you need to annotate the Product.Category property with the AssociationAttribute and IncludeAttribute. You should add a new metadata class for the Product entity, as follows:
using System.ComponentModel.DataAnnotations;
using System.ServiceModel.DomainServices.Server;
namespace WcfRiaRelationships.Web
{
[MetadataTypeAttribute(typeof(Product.ProductMetadata))]
public partial class Product
{
internal sealed class ProductMetadata
{
[Include]
// [Association("The name of the property",
//"Product.CategoryID (the foreign key)",
//"Category.CategoryID (the other side primary key")]
[Association("Category", "CategoryID", "CategoryID")]
public Category Category { get; set; }
}
}
}
Imports System.ComponentModel.DataAnnotations
Imports System.ServiceModel.DomainServices.Server
<MetadataTypeAttribute(GetType(Product.ProductMetadata))>
Partial Public Class Product
Friend NotInheritable Class ProductMetadata
' <Association("The name of the property",
' "Product.CategoryId (the foreign key)",
' "Category.CategoryID (the other side primary key")>
<Include(), Association("Category", "CategoryID", "CategoryID")>
Public Property Category() As Category
End Class
End Class
The AssociationAttribute is used to represent an association in the database, such as the relationship between a foreign key and a primary key. It informs WCF RIA that the Category property can be reconstructed on the client by comparing the Product.CategoryId (the foreign key) to the Category.CategoryId( the other side primary key). When applied to an entity association, the IncludeAttribute indicates that the association should be part of any code generated client entities, and that any related entities should be included when serializing results to the client.
Now you have the association between your domain classes included in the generated Product class on the client (e.g. Silverlight) side.
NorthwindDomainContext dbContext = new NorthwindDomainContext();
Category category = new Category();
category.CategoryName = "My Category";
category.Description = "Some Description";
Product product = new Product();
product.Discontinued = false;
product.ProductName = "My Product";
product.Category = category;
// It is enough just to pass the root object in the hierarchy.
dbContext.Products.Add(product);
// dbContext.Categories.Add(category); --- not needed.
dbContext.SubmitChanges();
Dim dbContext As New DomainService1()
Dim _category As New Category()
_category.CategoryName = "My Category"
_category.Description = "Some Description"
Dim _product As New Product()
_product.Discontinued = False
_product.ProductName = "My Product"
_product.Category = _category
' It is enough just to pass the root object in the hierarchy.
dbContext.Products.Add(_product)
' dbContext.Categories.Add(category); --- not needed.
dbContext.SubmitChanges()
Many-to-Many Relationships
WCF RIA data services does not support Many-to-Many relationships out of the box. The only possibility for now is to reverse map your join table and use it as a real entity. This way you will be able to do a One-To-Many associations from your two other entities to the join table.
Consider the following many-to-many relationship between the Employee and Territory entities from the Northwind database.
In order to generate persistent type for the join table, you need to perform the following steps:
- Delete the existing many-to-many relation.
- Open the Model Schema Explorer and expand the Tables node.
- Select the join table (in this case it will be EmployeeTerritories). Press F4 to open the Properties pane and set the IsJoinTable property to False.
- Navigate back to the Model Schema Explorer. Select the join table, right-click to open the context menu and select Map to persistent type.
Finally, your diagram should be similar to the snapshot below:
There are several specific things that should be pointed out here. If you have already created your domain service, then you have to re-generate it. However in this case, when you generate the new domain service, you have to choose the From Rlinq File option in the Choose Telerik Data Access Domain Model Type dialog.
Once your domain service is generated, you need to create a metadata class for the EmployeeTerritory entity and decorate the Employee and Territory properties with AssociationAttribute and IncludeAttribute.
using System.ComponentModel.DataAnnotations;
using System.ServiceModel.DomainServices.Server;
namespace WcfRiaRelationships.Web
{
[MetadataTypeAttribute(typeof(EmployeeTerritory.EmployeeTerritoryMetadata))]
public partial class EmployeeTerritory
{
internal sealed class EmployeeTerritoryMetadata
{
[Include]
[Association("Employee", "EmployeeID", "EmployeeID")]
public Employee Employee { get; set; }
[Include]
[Association("Territory", "TerritoryID", "TerritoryID")]
public Territory Territory { get; set; }
}
}
}
Imports System.ComponentModel.DataAnnotations
Imports System.ServiceModel.DomainServices.Server
<MetadataTypeAttribute(GetType(EmployeeTerritory.EmployeeTerritoryMetadata))>
Partial Public Class EmployeeTerritory
Friend NotInheritable Class EmployeeTerritoryMetadata
<Include(), Association("Employee", "EmployeeID", "EmployeeID")>
Public Property Employee() As Employee
<Include(), Association("Territory", "TerritoryID", "TerritoryID")>
Public Property Territory() As Territory
End Class
End Class
In the client application (e.g. Silverlight application), you could use the generated proxy association in the following manner:
NorthwindDomainContext domainService = new NorthwindDomainContext();
Employee employee = new Employee();
employee.FirstName = "Petar";
employee.LastName = "Ivanov";
Territory territory = new Territory();
territory.TerritoryID = "MyTerritory2";
territory.TerritoryDescription = "Some description";
territory.RegionID = 1;
EmployeeTerritory empTer = new EmployeeTerritory();
empTer.EmployeeID = employee.EmployeeID;
empTer.Employee = employee;
empTer.TerritoryID = territory.TerritoryID;
empTer.Territory = territory;
domainService.EmployeeTerritories.Add(empTer);
domainService.SubmitChanges();
Dim domainService As New NorthwindDomainContext()
Dim _employee As New Employee()
_employee.FirstName = "Petar"
_employee.LastName = "Ivanov"
Dim _territory As New Territory()
_territory.TerritoryID = "MyTerritory2"
_territory.TerritoryDescription = "Some description"
_territory.RegionID = 1
Dim empTer As New EmployeeTerritory()
empTer.EmployeeID = _employee.EmployeeID
empTer.Employee = _employee
empTer.TerritoryID = _territory.TerritoryID
empTer.Territory = _territory
domainService.EmployeeTerritories.Add(empTer)
domainService.SubmitChanges()