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

Many-to-Many Associations

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 teaches you how to create many-to-many artificial associations, as follows:

You need to use/import the Telerik.OpenAccess.Metadata.Fluent.Artificial namespace.

Configuring Many-To-Many Associations

This section describes how to create a many-to-many association between artificial types. The following example declares two artificial types - Employee and Territory. Several primitive properties are declared for each of the types. Finally, a many-to-many association is created between the Employee and Territory types. Creating many-to-many associations is very similar to the one-to-many associations. Again, you should explicitly declare both of the association ends. However, in this case you have to use only the HasArtificialCollectionAssociation and WithOppositeCollection methods. Below is the source code demonstrating the creation of a many-to-many association between artificial types. Sample FluentMetadataSource implementation could be found at the end of the topic.

employeeConfiguration.
   HasArtificialCollectionAssociation( "Territories", territoryConfiguration.ConfiguredType ).
   WithOppositeCollection( "Employees" );
territoryConfiguration.
   HasArtificialCollectionAssociation( "Employees", employeeConfiguration.ConfiguredType ).
   WithOppositeCollection( "Territories" )
employeeConfiguration.
    HasArtificialCollectionAssociation("Territories", territoryConfiguration.ConfiguredType).
    WithOppositeCollection("Employees")
territoryConfiguration.
    HasArtificialCollectionAssociation("Employees", employeeConfiguration.ConfiguredType).
    WithOppositeCollection("Territories")

Specifying the Join Table

In the previous example Telerik Data Access will create a new join table with default name "employee_territory".

However, you have the option to explicitly specify the join table by using the MapJoinTable method.

employeeConfiguration.
   HasArtificialCollectionAssociation( "Territories", territoryConfiguration.ConfiguredType ).
   WithOppositeCollection( "Employees" ).MapJoinTable("EmployeeTerritory");
territoryConfiguration.
   HasArtificialCollectionAssociation( "Employees", employeeConfiguration.ConfiguredType ).
   WithOppositeCollection( "Territories" ).MapJoinTable( "EmployeeTerritory" );
employeeConfiguration.
    HasArtificialCollectionAssociation("Territories", territoryConfiguration.ConfiguredType).
    WithOppositeCollection("Employees").MapJoinTable("EmployeeTerritory")
territoryConfiguration.
    HasArtificialCollectionAssociation("Employees", employeeConfiguration.ConfiguredType).
    WithOppositeCollection("Territories").MapJoinTable("EmployeeTerritory")

Creating Many-To-Many Self-Reference Associations

This section describes how to create a many-to-many self-reference association between artificial types. In the following example one artificial type is declared - Employee. For the purpose of this walkthrough some artificial properties are created. The creation of a many-to-many self-reference association is very similar to the creation of a regular many-to-many association. However, in this case the configured type is one and the same. The code below demonstrates the creation of many-to-many self-reference associations. Sample FluentMetadataSource implementation could be found at the end of the topic.

employeeConfiguration.
   HasArtificialCollectionAssociation( "ReportsTo", employeeConfiguration.ConfiguredType ).
   WithOppositeCollection( "Employees" );
employeeConfiguration.
   HasArtificialCollectionAssociation( "Employees", employeeConfiguration.ConfiguredType ).
   WithOppositeCollection( "ReportsTo" );
employeeConfiguration.
    HasArtificialCollectionAssociation("ReportsTo", employeeConfiguration.ConfiguredType).
    WithOppositeCollection("Employees")
employeeConfiguration.
    HasArtificialCollectionAssociation("Employees", employeeConfiguration.ConfiguredType).
    WithOppositeCollection("ReportsTo")

Creating Many-To-Many Associations Between an Existing Type And an Artificial Type

This section describes how to create a many-to-many association between a CLR type (the Employee class) and an artificial type. Since the Employee type is a real type, you have to use the generic MappingConfiguration<T> class. A new artificial type named Territory with one artificial property (Name) is created. Next, a new many-to-many association is created between the Employee and Territory. The process of creation of a many-to-many association in this case is absolutely the same as the creation of a many-to-many association between artificial types. The only difference is that you have to use the generic MappingConfiguration<T> class for the real type. Sample FluentMetadataSource implementation could be found at the end of the topic.

employeeConfiguration.
   HasArtificialCollectionAssociation( "Territories", territoryConfiguration.ConfiguredType ).
   WithOppositeCollection( "Employees" );
territoryConfiguration.
   HasArtificialCollectionAssociation( "Employees", employeeConfiguration.ConfiguredType ).
   WithOppositeCollection( "Territories" );
employeeConfiguration.
    HasArtificialCollectionAssociation("Territories", territoryConfiguration.ConfiguredType).
    WithOppositeCollection("Employees")
territoryConfiguration.
    HasArtificialCollectionAssociation("Employees", employeeConfiguration.ConfiguredType).
    WithOppositeCollection("Territories")

Creating Many-To-Many Artificial Associations Between Existing Types

The next example demonstrates how to create a new many-to-many artificial association between the Employee and Territory classes. The process of creation of a many-to-many association in this case is absolutely the same as the creation of a many-to-many association between artificial types. The only difference is that you have to use the generic MappingConfiguration<T> class for the real types. Sample FluentMetadataSource implementation could be found at the end of the topic.

employeeConfiguration.
   HasArtificialCollectionAssociation( "Territories", territoryConfiguration.ConfiguredType ).
   WithOppositeCollection( "Employees" );
territoryConfiguration.
   HasArtificialCollectionAssociation( "Employees", employeeConfiguration.ConfiguredType ).
   WithOppositeCollection( "Territories" );
employeeConfiguration.
    HasArtificialCollectionAssociation("Territories", territoryConfiguration.ConfiguredType).
    WithOppositeCollection("Employees")
territoryConfiguration.
    HasArtificialCollectionAssociation("Employees", employeeConfiguration.ConfiguredType).
    WithOppositeCollection("Territories")

TestClass

Currently one of the limitations of Telerik Data Access is that you cannot have a Telerik Data Access project running without at least one persistent type defined. This basically means that you have to have a plain CLR type that is part of your model in order to run a Telerik Data Access project. There is no way to run Telerik Data Access entirely on artificial types. The simplest solution in this case is to add a dummy class to your model.

public class TestClass
{
   public int ID {get;set;}
}
Public Class TestClass
    Private _iD As Integer
    Public Property ID() As Integer
        Get
            Return _iD
        End Get
        Set(ByVal value As Integer)
            _iD = value
        End Set
    End Property
End Class

Sample FluentMetadataSource Implementation - Many-to-Many Artificial Associations

public class FluentModelMetadataSource : FluentMetadataSource
{
   protected override IList<MappingConfiguration> PrepareMapping()
   {
       List<MappingConfiguration> configurations = new List<MappingConfiguration>();

       MappingConfiguration<TestClass> testConfiguration = new MappingConfiguration<TestClass>();
       testConfiguration.MapType();
       testConfiguration.HasProperty( x => x.ID ).IsIdentity();

       MappingConfiguration employeeConfiguration = new MappingConfiguration( "Employee", "EmployeeNamespace" );
       employeeConfiguration.HasArtificialPrimitiveProperty<Int32>( "Id" ).IsIdentity();
       employeeConfiguration.HasArtificialStringProperty( "Name" );
       employeeConfiguration.HasArtificialPrimitiveProperty<DateTime>( "HireDate" );

       MappingConfiguration territoryConfiguration = new MappingConfiguration( "Territory", "TerritoryNamespace" );
       territoryConfiguration.HasArtificialPrimitiveProperty<Int32>( "Id" ).IsIdentity();
       territoryConfiguration.HasArtificialStringProperty( "Name" );

       employeeConfiguration.
           HasArtificialCollectionAssociation( "Territories", territoryConfiguration.ConfiguredType ).
           WithOppositeCollection( "Employees" ).MapJoinTable( "EmployeeTerritory" );
       territoryConfiguration.
           HasArtificialCollectionAssociation( "Employees", employeeConfiguration.ConfiguredType ).
           WithOppositeCollection( "Territories" ).MapJoinTable( "EmployeeTerritory" );

       configurations.Add( employeeConfiguration );
       configurations.Add( territoryConfiguration );
       configurations.Add( testConfiguration );

       return configurations;
   }
}
Public Class FluentModelMetadataSource
    Inherits FluentMetadataSource
    Protected Overrides Function PrepareMapping() As _
        System.Collections.Generic.IList(Of Telerik.OpenAccess.Metadata.Fluent.MappingConfiguration)
        Dim configurations As List(Of MappingConfiguration) = New List(Of MappingConfiguration)()

        Dim testConfiguration As New MappingConfiguration(Of TestClass)()
        testConfiguration.MapType()
        testConfiguration.HasProperty(Function(x) x.ID).IsIdentity()
        testConfiguration.FieldNamingRules.AddPrefix = "_"

        Dim employeeConfiguration As New MappingConfiguration("Employee", "EmployeeNamespace")
        employeeConfiguration.HasArtificialPrimitiveProperty(Of Int32)("Id").IsIdentity()
        employeeConfiguration.HasArtificialStringProperty("Name")
        employeeConfiguration.HasArtificialPrimitiveProperty(Of Date)("HireDate")

        Dim territoryConfiguration As New MappingConfiguration("Territory", "TerritoryNamespace")
        territoryConfiguration.HasArtificialPrimitiveProperty(Of Int32)("Id").IsIdentity()
        territoryConfiguration.HasArtificialStringProperty("Name")

        employeeConfiguration.
            HasArtificialCollectionAssociation("Territories", territoryConfiguration.ConfiguredType).
            WithOppositeCollection("Employees").MapJoinTable("EmployeeTerritory")
        territoryConfiguration.
            HasArtificialCollectionAssociation("Employees", employeeConfiguration.ConfiguredType).
            WithOppositeCollection("Territories").MapJoinTable("EmployeeTerritory")

        configurations.Add(employeeConfiguration)
        configurations.Add(territoryConfiguration)

        Return configurations
    End Function
End Class

Sample FluentMetadataSource Implementation - Many-to-Many Self-Reference Artificial Associations

public class FluentModelMetadataSource : FluentMetadataSource
{
   protected override IList<MappingConfiguration> PrepareMapping()
   {
       List<MappingConfiguration> configurations = new List<MappingConfiguration>();

       MappingConfiguration<TestClass> testConfiguration = new MappingConfiguration<TestClass>();
       testConfiguration.MapType();
       testConfiguration.HasProperty( x => x.ID ).IsIdentity();

       MappingConfiguration employeeConfiguration = new MappingConfiguration( "Employee", "EmployeeNamespace" );
       employeeConfiguration.HasArtificialStringProperty( "Name" );
       employeeConfiguration.HasArtificialPrimitiveProperty<Int32>( "Id" ).IsIdentity( KeyGenerator.Autoinc );

       employeeConfiguration.
           HasArtificialCollectionAssociation( "ReportsTo", employeeConfiguration.ConfiguredType ).
           WithOppositeCollection( "Employees" );
       employeeConfiguration.
           HasArtificialCollectionAssociation( "Employees", employeeConfiguration.ConfiguredType ).
           WithOppositeCollection( "ReportsTo" );

       configurations.Add( employeeConfiguration );
       configurations.Add( testConfiguration );

       return configurations;
   }
}
Public Class FluentModelMetadataSource
    Inherits FluentMetadataSource
    Protected Overrides Function PrepareMapping() As IList(Of MappingConfiguration)
        Dim configurations As New List(Of MappingConfiguration)()

        Dim testConfiguration As New MappingConfiguration(Of TestClass)()
        testConfiguration.MapType()
        testConfiguration.FieldNamingRules.AddPrefix = "_"
        testConfiguration.HasProperty(Function(x) x.ID).IsIdentity()

        Dim employeeConfiguration As New MappingConfiguration("Employee", "EmployeeNamespace")
        employeeConfiguration.HasArtificialStringProperty("Name")
        employeeConfiguration.HasArtificialPrimitiveProperty(Of Int32)("Id").IsIdentity(KeyGenerator.Autoinc)

        employeeConfiguration.
            HasArtificialCollectionAssociation("ReportsTo", employeeConfiguration.ConfiguredType).
            WithOppositeCollection("Employees")
        employeeConfiguration.
            HasArtificialCollectionAssociation("Employees", employeeConfiguration.ConfiguredType).
            WithOppositeCollection("ReportsTo")

        configurations.Add(employeeConfiguration)
        configurations.Add(testConfiguration)

        Return configurations
    End Function
End Class

Employee Class

public class Employee
{
   public int Id {get;set;}
   public string Name {get;set;}
}
Public Class Employee
    Private _id As Integer
    Public Property Id() As Integer
        Get
            Return _id
        End Get
        Set(ByVal value As Integer)
            _id = value
        End Set
    End Property

    Private _name As String
    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
        End Set
    End Property
End Class

Sample FluentMetadataSource Implementation - Many-To-Many Associations Between an Existing Type and an Artificial Type

public class FluentModelMetadataSource : FluentMetadataSource
{
   protected override IList<MappingConfiguration> PrepareMapping()
   {
       List<MappingConfiguration> configurations = new List<MappingConfiguration>();

       MappingConfiguration<Employee> employeeConfiguration = new MappingConfiguration<Employee>();
       employeeConfiguration.MapType( e => new
                                               {
                                                   EmployeeId = e.Id,
                                                   EmplName = e.Name
                                               } ).ToTable( "Employees" );
       employeeConfiguration.HasProperty( e => e.Id ).IsIdentity();

       MappingConfiguration territoryConfiguration = new MappingConfiguration( "Territory", "TerritoryNamespace" );
       territoryConfiguration.HasArtificialStringProperty( "Name" );
       territoryConfiguration.HasArtificialPrimitiveProperty<int>( "Id" ).IsIdentity();

       employeeConfiguration.
           HasArtificialCollectionAssociation( "Territories", territoryConfiguration.ConfiguredType ).
           WithOppositeCollection( "Employees" );
       territoryConfiguration.
           HasArtificialCollectionAssociation( "Employees", employeeConfiguration.ConfiguredType ).
           WithOppositeCollection( "Territories" );

       configurations.Add( employeeConfiguration );
       configurations.Add( territoryConfiguration );

       return configurations;
   }
}
Public Class FluentModelMetadataSource
    Inherits FluentMetadataSource
    Protected Overrides Function PrepareMapping() As IList(Of MappingConfiguration)
        Dim configurations As New List(Of MappingConfiguration)()

        Dim employeeConfiguration As New MappingConfiguration(Of Employee)()
        employeeConfiguration.MapType(Function(e) New With {Key .EmployeeId = e.Id, Key .EmplName = e.Name}).ToTable("Employees")
        employeeConfiguration.HasProperty(Function(e) e.Id).IsIdentity()
        employeeConfiguration.FieldNamingRules.AddPrefix = "_"

        Dim territoryConfiguration As New MappingConfiguration("Territory", "TerritoryNamespace")
        territoryConfiguration.HasArtificialStringProperty("Name")
        territoryConfiguration.HasArtificialPrimitiveProperty(Of Integer)("Id").IsIdentity()

        employeeConfiguration.
            HasArtificialCollectionAssociation("Territories", territoryConfiguration.ConfiguredType).
            WithOppositeCollection("Employees")
        territoryConfiguration.
            HasArtificialCollectionAssociation("Employees", employeeConfiguration.ConfiguredType).
            WithOppositeCollection("Territories")

        configurations.Add(employeeConfiguration)
        configurations.Add(territoryConfiguration)

        Return configurations
    End Function
End Class

Territory Class

public class Territory
{
   public int Id { get; set; }
   public string Name {get;set;}
}
Public Class Territory
    Private _id As Integer
    Public Property Id() As Integer
        Get
            Return _id
        End Get
        Set(ByVal value As Integer)
            _id = value
        End Set
    End Property

    Private _name As String
    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
        End Set
    End Property
End Class

Sample FluentMetadataSource Implementation - Many-To-Many Artificial Associations Between Existing Types

public class FluentModelMetadataSource : FluentMetadataSource
{
   protected override IList<MappingConfiguration> PrepareMapping()
   {
       List<MappingConfiguration> configurations = new List<MappingConfiguration>();

       MappingConfiguration<Employee> employeeConfiguration = new MappingConfiguration<Employee>();
       employeeConfiguration.MapType( e => new
                                               {
                                                   EmployeeId = e.Id,
                                                   EmplName = e.Name
                                               } ).ToTable( "Employees" );
       employeeConfiguration.HasProperty( e => e.Id ).IsIdentity();

       MappingConfiguration<Territory> territoryConfiguration = new MappingConfiguration<Territory>();
       territoryConfiguration.MapType( t => new
                                               {
                                                   TerritoryId = t.Id,
                                                   TerritoryName = t.Name
                                               } ).ToTable( "Territories" );
       territoryConfiguration.HasProperty( e => e.Id ).IsIdentity();

       employeeConfiguration.
           HasArtificialCollectionAssociation( "Territories", territoryConfiguration.ConfiguredType ).
           WithOppositeCollection( "Employees" );
       territoryConfiguration.
           HasArtificialCollectionAssociation( "Employees", employeeConfiguration.ConfiguredType ).
           WithOppositeCollection( "Territories" );

       configurations.Add( employeeConfiguration );
       configurations.Add( territoryConfiguration );
       return configurations;
   }
}
Public Class FluentModelMetadataSource
    Inherits FluentMetadataSource
    Protected Overrides Function PrepareMapping() As IList(Of MappingConfiguration)
        Dim configurations As New List(Of MappingConfiguration)()

        Dim employeeConfiguration As New MappingConfiguration(Of Employee)()
        employeeConfiguration.MapType(Function(e) _
            New With {Key .EmployeeId = e.Id, Key .EmplName = e.Name}).ToTable("Employees")
        employeeConfiguration.HasProperty(Function(e) e.Id).IsIdentity()
        employeeConfiguration.FieldNamingRules.AddPrefix = "_"

        Dim territoryConfiguration As New MappingConfiguration(Of Territory)()
        territoryConfiguration.MapType(Function(t) _
            New With {Key .TerritoryId = t.Id, Key .TerritoryName = t.Name}).ToTable("Territories")
        territoryConfiguration.HasProperty(Function(e) e.Id).IsIdentity()
        territoryConfiguration.FieldNamingRules.AddPrefix = "_"

        employeeConfiguration.
            HasArtificialCollectionAssociation("Territories", territoryConfiguration.ConfiguredType).
            WithOppositeCollection("Employees")
        territoryConfiguration.
            HasArtificialCollectionAssociation("Employees", employeeConfiguration.ConfiguredType).
            WithOppositeCollection("Territories")

        configurations.Add(employeeConfiguration)
        configurations.Add(territoryConfiguration)

        Return configurations
    End Function
End Class