Data Access has been discontinued. Please refer to this page for more information.

JoinTableAssociationAttribute

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.

The JoinTableAssociationAttribute is used to designate a property to represent a relationship that is exposed via a join table in the database (many-to-many relationship). The JoinTableAssociationAttribute derives from AssociationAttribute and exposes the following properties:

  • TableName - represents the name of the join table.
  • OwnerColumns - represents a coma separated list of the columns of the join table that point to the owner side of the association.
  • TargetColumns - represents a coma separated list of the columns of the join table that point to the destination side of the association.
  • SequenceColumn - represents the name of the column that is used for ordering the associated objects if such column exists.
  • SourceConstraint - the name of the constraint for the columns of the join table that point to the owner side of the association.
  • TargetConstraint - the name of the constraint for the columns of the join table that point to the destination side of the association.

You can encode as property references in your persistent class any relationships that will always be the same. In the Northwind sample database, for example, because employees typically have many territories (and vice versa), there is an additional table, which represents the many-to-many relationship.

In many-to-many relationships, the primary key of the link table (also named the junction table) is often formed by a composite of the foreign keys from the other two tables.

In the following many-to-many example, the Employee class has a property that declares the relationship between an employee and its territories. The JoinTableAssociationAttribute attribute which marks the Territories property describes how this association is accomplished. Namely, by specifying the join table name (TableName), the owner side columns (OwnerColums), the destination side columns (TargetColumns) and the constraints (SourceConstraint and TargetConstraint).

namespace AttributesDemo
{
    [Table("Employees")]
    [KeyGenerator(KeyGenerator.Autoinc)]
    public partial class Employee
    {  
     private IList<Territory> _territories = new List<Territory>();
     [JoinTableAssociation(TableName = "EmployeeTerritories", OwnerColumns = "EmployeeID",
     TargetColumns = "TerritoryID", SourceConstraint = "FK_EmployeeTerritories_Employees", 
     TargetConstraint = "FK_EmployeeTerritories_Territories")]
     [Column("EmployeeID", OpenAccessType = OpenAccessType.Int32, IsPrimaryKey = true, Length = 0, 
     Scale = 0, SqlType = "int")]
     [Column("TerritoryID", OpenAccessType = OpenAccessType.Varchar, IsPrimaryKey = true, 
     Length = 20, Scale = 0, SqlType = "nvarchar")]
     [Storage("_territories")]
     public virtual IList<Territory> Territories
     {
         get
         {
             return this._territories;
         }
     }

    }
}
Namespace AttributesDemo
 <Table("Employees"), KeyGenerator(KeyGenerator.Autoinc)>
 Partial Public Class Employee
  Private _territories As IList(Of Territory) = New List(Of Territory)()
  <JoinTableAssociation(TableName := "EmployeeTerritories", OwnerColumns := "EmployeeID", _ 
   TargetColumns := "TerritoryID", SourceConstraint := "FK_EmployeeTerritories_Employees", _
   TargetConstraint := "FK_EmployeeTerritories_Territories"), Column("EmployeeID", _
   OpenAccessType := OpenAccessType.Int32, IsPrimaryKey := True, Length := 0, Scale := 0, _
   SqlType := "int"), Column("TerritoryID", OpenAccessType := OpenAccessType.Varchar, _
   IsPrimaryKey := True, Length := 20, Scale := 0, SqlType := "nvarchar"), _
   Storage("_territories")>
  Public Overridable ReadOnly Property Territories() As IList(Of Territory)
   Get
    Return Me._territories
   End Get
  End Property
 End Class
End Namespace

Note that in the previous example multiple ColumnAttributes are specified. The multiple column attributes are actually holding the information about the columns of the join table. So in the previous example the Territories property reflects the join-table relationship between Employees and Territories. The "EmployeeID" column points to the owner end of the association (e.g. Employees table). The "TerritoryID" column points to the target end of the association (e.g. Territories table). Then two ColumnAttributes are used to give detailed description of the above mentioned columns. In that way, by having the name of the join-table and the two columns that are part of it, detail information about the join table is provided.

You can also reverse the situation. Instead of using the Employee class to describe the association between employees and territories, you can use the Territory class. The Territory class uses the CollectionAttribute to describe the relationship back to the employee, as in the following code example.

class Territory
{
   private IList<Employee> employees;
   [Collection( InverseProperty = "territories" )]
   [Storage( "employees" )]
   public IList<Employee> Employees
   {
       get
       {
           return employees;
       }
   }
}
Friend Class Territory
 Private _employees As IList(Of Employee)
 <Collection(InverseProperty := "territories"), Storage("employees")>
 Public ReadOnly Property Employees() As IList(Of Employee)
  Get
   Return _employees
  End Get
 End Property
End Class