Telerik OpenAccess Classic

Telerik OpenAccess ORM Send comments on this topic.
Many-to-many Mapping
See Also
Programmer's Guide > OpenAccess ORM Classic (Old API) > Programming With OpenAccess > Mapping > Map and Collection Fields > Many-to-many Mapping

Glossary Item Box

This documentation article is a legacy resource describing the functionality of the deprecated OpenAccess Classic only. The contemporary documentation of Telerik OpenAccess ORM is available here.

OpenAccess ORM supports many-to-many mappings where two collections share a common link table. At commit or flush time the link table is updated to match only one side of the many-to-many (since any given element will be in both collections but only gets one row in the link table). Refer to inverse for details on the XML metadata required to setup a many-to-many relationship.

When an element is added or removed from one collection the other side must be updated by the application. Failure to keep the collections in sync may result in database updates being lost or stale data remaining in the 2nd level cache (Refer to 2nd Level Cache for more information).

If the non-inverse side of the relationship is an IList then the order of its elements is preserved by default using a sequence column in the link table (see List). The order of the inverse side of the relationship is not preserved.

Here is sample code for an unmanaged many-to-many relationship. A User has many Group references and a Group many User references:

C# Copy Code
[OpenAccess.Persistent]
public class Group
{
 
private string name;
 [OpenAccess.ItemType(
typeof(User) )]
 
private IList users = new ArrayList(); // of User
 
. . .
 
public void addUser( User u )
 {
   addUserImp( u );
   u.addGroupImp(
this );
 }

 
public void removeUser( User u )
 {
   removeUserImp( u );
   u.removeGroupImp(
this );
 }

 
void addUserImp( User u )
 {
   users.Add( u );
 }
 
void removeUserImp( User u )
 {
   users.Remove( u );
 }

 
public IList getUsers()
 {
   
return users;
 }
}

[OpenAccess.Persistent]
public class User
{
 
private string name;
 [OpenAccess.ItemType(
typeof(Group) )]
 
private IList groups = new ArrayList(); // inverse Group.users
 
. . .
 
public void addGroup( Group g )
 {
   addGroupImp( g );
   g.addUserImp(
this );
 }

 
public void removeGroup( Group g )
 {
   removeGroupImp( g );
   g.removeUserImp(
this );
 }

 
void addImp( Group g )
 {
   groups.Add( g );
 }
 
void removeImp( Group g ) {
   groups.Remove( g );
 }

 
public IList getGroups()
 {
   
return groups;
 }
}
VB .NET Copy Code
<OpenAccess.Persistent> _
Public Class Group
  Private name As String
  <OpenAccess.ItemType(GetType(User))> _
  Private users As IList = New ArrayList()
 ...
  Public Sub addUser(ByVal u As User)
 addUserImp(u)
 u.addGroupImp(Me)
 End Sub
  Public Sub removeUser(ByVal u As User)
 removeUserImp(u)
 u.removeGroupImp(Me)
  End Sub
  Private Sub addUserImp(ByVal u As User)
 users.Add(u)
  End Sub
  Private Sub removeUserImp(ByVal u As User)
 users.Remove(u)
  End Sub
  Public Function getUsers() As IList
 Return users
  End Function
End Class
<OpenAccess.Persistent> _
Public Class User
  Private name As String
  <OpenAccess.ItemType(GetType(Group))> _
  Private groups As IList = New ArrayList()
 ...
  Public Sub addGroup(ByVal g As Group)
 addGroupImp(g)
 g.addUserImp(Me)
 End Sub
  Public Sub removeGroup(ByVal g As Group)
 removeGroupImp(g)
 g.removeUserImp(Me)
  End Sub
  Private Sub addImp(ByVal g As Group)
 groups.Add(g)
  End Sub
  Private Sub removeImp(ByVal g As Group)
 groups.Remove(g)
  End Sub
  Public Function getGroups() As IList
 Return groups
  End Function
End Class

Given below is the corresponding XML metadata. Note that the ordering extension is used to ensure that Group.users is sorted by user name. The sequence column in the link table preserves the order of User.groups.

Copy Code
<class name="User">
 
<field name="groups">
   
<collection></collection> <!-- ItemType is Group -->
 
</field>
</
class>

<
class name="Groups">
 
<field name="users">
   
<collection> <!-- ItemType is User -->
     
<extension key="inverse" value="groups" />
     
<extension key="ordering" value="name ascending" />
   
</collection>
 
</field>
</
class>

Managed Many-to-many

When an element is added or removed from one collection the other side is automatically kept in sync and vice versa. The relationship is maintained by the tracked collection implementation of the collections on either side. (See inverse for details of the XML metadata that is required). If you delete an instance using the IObjectScope method Remove() it will automatically be removed from the other side of any many-to-many relationships it is part of.

The relationship is not maintained for transient instances as the tracked implementations are only set on managed instances. However, when transient instances are made persistent, the relationship is completed.

Example code for a managed many-to-many relationship:

C# Copy Code
[OpenAccess.Persistent]
public class Group
{
 
private string name;
 [OpenAccess.ItemType(
typeof(User) )]
 
private IList users = new ArrayList(); // of User
 
. . .
 
public void addUser( User u )
 {
   users.add( u );
   
// the tracked collection implementation
   
// will make sure that u.groups.contains(this)
 }

 
public void removeUser( User u )
 {
   users.remove( u );
   
// the tracked collection implementation
   
// will make sure that !u.groups.contains(this)
 }

 
public IList getUsers()
 {
   
return users;
 }
}

[OpenAccess.Persistent]
public class User
{
 
private string name;
 [OpenAccess.ItemType(
typeof(Group) )]
 
private IList groups = new ArrayList(); // inverse Group.users
 
. . .
 
public void addGroup( Group g )
 {
   groups.Add( g );
   
// the tracked collection implementation
   
// will make sure that g.users.contains(this)
 }

 
public void removeGroup( Group g )
 {
   groups.Remove( g );
   
// the tracked collection implementation
   
// will make sure that !g.users.contains(this)
 }

 
public IList getGroups()
 {
   
return groups;
 }
}  

 

VB .NET Copy Code
<OpenAccess.Persistent> _
Public Class Group
  Private name As String
  <OpenAccess.ItemType(GetType(User))> _
  Private users As IList = New ArrayList()
 ...
  Public Sub addUser(ByVal u As User)
 users.add(u)
 ' the tracked collection implementation
 ' will make sure that u.groups.contains(this)
 End Sub
  Public Sub removeUser(ByVal u As User)
 users.remove(u)
 ' the tracked collection implementation
 ' will make sure that !u.groups.contains(this)
  End Sub
  Public Function getUsers() As IList
 Return users
  End Function
End Class
<OpenAccess.Persistent> _
Public Class User
  Private name As String
  <OpenAccess.ItemType(GetType(Group))> _
  Private groups As IList = New ArrayList()
 ...
  Public Sub addGroup(ByVal g As Group)
 groups.Add(g)
 ' the tracked collection implementation
 ' will make sure that g.users.contains(this)
 End Sub
  Public Sub removeGroup(ByVal g As Group)
 groups.Remove(g)
 ' the tracked collection implementation
 ' will make sure that !g.users.contains(this)
  End Sub
  Public Function getGroups() As IList
 Return groups
  End Function
End Class

If you flag a many-to-many relationship as managed and you complete the relationship in your code then all lists will get duplicate entries. It is too expensive for the tracked collection implementations to guard against this.

The XML metadata for this example is as follows:

Copy Code
<class name="User">
 
<field name="groups">
   
<collection> <!-- ItemType is Group -->
     
<extension key="managed" value="true" />
   
</collection>
 
</field>
</
class>

<
class name="Groups">
 
<field name="users">
   
<collection> <!-- ItemType is User -->
     
<extension key="inverse" value="groups" />
     
<extension key="ordering" value="name ascending" />
   
</collection>
 
</field>
</
class>

See Also