This post explains the nuances of implementing many-to-many relationship between non-persistent Domain Components when working with Xafari x07 framework. To see the first part of this investigation, refer to the About non-persistent domain components - Xafari Framework post.

There are 3 combinations of relationship from the viewpoint of persistence:

Persistent, Persistent

This is the standard situation, it will not be considered since this situation is described in detail in the XAF documentation.

NonPersistent, NonPersistent

The figure below illustrates this situation.

As you can see, there are two non-persistent entities named ObjectM and ObjectN, and entities participate in an N:M (Many-To-Many) relationship. The problem is to implement this relationship between ObjectM and ObjectN.

The problem will be solved in different ways depending on several variants of relationship:

Strongly-typed persistence collections

An example below demonstrates how to use typed persistence collections to implement the relationship.

This method assumes that the relationship between the original objects ObjectM and ObjectN may be transformed into the relationship between the heirs PersistentObjectM and PersistentObjectN. This is a limitation, because in this case it is impossible to add other direct ObjectM/ObjectN descendants to the PersistentTermObjectNs/ PersistentTermObjectMs collections.

The code snippet below demonstrates implementation for such variant:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[DomainComponent, NonPersistentDc]
public interface ObjectN
{
    string String1 { get; set; }
    [NonPersistentDc]
    IList TermObjectMs { get; }
}
[DomainLogic(typeof(ObjectN))]
public class ObjectNLogic : DomainLogicBase
{
    public ObjectNLogic(ObjectN instance): base(instance){}
    public IList TermObjectMs
    {
        get { return this.Instance.GetListValueByTerm(x => x.TermObjectMs); }
    }
}
[DomainComponent]
public interface PersistentObjectN : ObjectN
{
    [Term("TermObjectMs")]
    IList PersistentTermObjectMs { get; }
}
[DomainComponent, NonPersistentDc]
public interface ObjectM
{
    string String1 { get; set; }
    [NonPersistentDc]
    IList TermObjectNs { get; }
}
[DomainLogic(typeof(ObjectM))]
public class ObjectMLogic : DomainLogicBase
{
    public ObjectMLogic(ObjectM instance): base(instance){}
    public IList TermObjectNs
    {
        get { return this.Instance.GetListValueByTerm(x => x.TermObjectNs); }
    }
}
[DomainComponent]
public interface PersistentObjectM : ObjectM
{
    [Term("TermObjectNs")]
    IList PersistentTermObjectNs { get; }
}

The main idea is to describe ObjectN:ObjectM relation using calculated properties (TermObjectNs /TermObjectMs), add persistent collections (PersistentTermObjectNs /PersistentTermObjectMs) to descendants and use these collections when calculate TermObjectNs /TermObjectMs.

Xafari provides a standard way to describe calculated fields using Xafari Terms mechanism.

Non-typed persistence collections

An example below demonstrates how to use non-typed persistence collections to implement the relationship.

To implement relationship between ObjectM and ObjectN objects, you can use a special class that exposes non-typed reference (weak reference) to persistent descendants. Unlike the previous variant, in this case, it is possible to add any heirs of ObjectM / ObjectN to the collection. However, when you edit these collections, there are limitations in the UI.

Relation objects are stored in one table

The code snippet below demonstrates implementation for such variant:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[DomainComponent, NonPersistentDc]
public interface ObjectN
{
    string String1 { get; set; }
    [NonPersistentDc]
    [XPWeakManyToMany(XPWeakManyToManyAttribute.RelationRole.Right)]
    [BackReferenceProperty("WeakObjectNs")]
    IList WeakObjectMs { get; }
}
[DomainLogic(typeof(ObjectN))]
public class ObjectNLogic : DomainLogicBase
{
    public ObjectNLogic(ObjectN instance) : base(instance){}
    public IList WeakObjectMs
    {
        get { return this.GetWeakList(x => x.WeakObjectMs); }
    }
}
[DomainComponent]
public interface PersistentObjectN : ObjectN {}
[DomainComponent, NonPersistentDc]
public interface ObjectM
{
    string String1 { get; set; }
    [NonPersistentDc]
    [XPWeakManyToMany(XPWeakManyToManyAttribute.RelationRole.Left)]
    [BackReferenceProperty("WeakObjectMs")]
    IList WeakObjectNs { get; }
}
[DomainLogic(typeof(ObjectM))]
public class ObjectMLogic : DomainLogicBase
{
    public ObjectMLogic(ObjectM instance): base(instance){}
    public IList WeakObjectNs
    {
        get { return this.GetWeakList(x => x.WeakObjectNs); }
    }
}
[DomainComponent]
public interface PersistentObjectM : ObjectM {}

You also need to update the metadata by adding the following code to the module:

1
2
3
4
5
6
public override void CustomizeTypesInfo(ITypesInfo typesInfo)
{
    base.CustomizeTypesInfo(typesInfo);
    XPWeakManyToMany.CustomizeTypesInfo(typesInfo, typeof(ObjectN));
    XPWeakManyToMany.CustomizeTypesInfo(typesInfo, typeof(ObjectM));
}

XPWeakManyToMany.CustomizeTypesInfo() method adds the required metadata to correct work of the Many-To-Many relationship.

Relation objects are stored in separate tables

There may be situations when it is impossible to store all the relationships in one table. In this case, we need to describe an additional persistent object to store N: M objects. The example below demonstrates this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[DomainComponent, NonPersistentDc]
public interface ObjectN
{
    string String1 { get; set; }
    [NonPersistentDc]
    [XPWeakManyToMany(XPWeakManyToManyAttribute.RelationRole.Right, typeof(OwnIntermediateTable))]
    [BackReferenceProperty("WeakObjectNs")]
    IList WeakObjectMs { get; }
}
[DomainLogic(typeof(ObjectN))]
public class ObjectNLogic : DomainLogicBase
{
    public ObjectNLogic(ObjectN instance) : base(instance){}
    public IList WeakObjectMs
    {
        get { return this.GetWeakList(x => x.WeakObjectMs); }
    }
}
[DomainComponent]
public interface PersistentObjectN : ObjectN {}
[DomainComponent, NonPersistentDc]
public interface ObjectM
{
    string String1 { get; set; }
    [NonPersistentDc]
    [XPWeakManyToMany(XPWeakManyToManyAttribute.RelationRole.Left, typeof(OwnIntermediateTable))]
    [BackReferenceProperty("WeakObjectMs")]
    IList WeakObjectNs { get; }
}
[DomainLogic(typeof(ObjectM))]
public class ObjectMLogic : DomainLogicBase
{
    public ObjectMLogic(ObjectM instance): base(instance){}
    public IList WeakObjectNs
    {
        get { return this.GetWeakList(x => x.WeakObjectNs); }
    }
}
[DomainComponent]
public interface PersistentObjectM : ObjectM {}
[DeferredDeletion(false)]
public class OwnIntermediateTable : XPWeakManyToManyBase
{
    public OwnIntermediateTable(Session session) : base(session){}
}

The code above contains a special persistent OwnIntermediateTable class. The corresponding table will store only WeakObjectMs / WeakObjectNs relationship. You also need to update the metadata by adding the following code to the module:

1
2
3
4
5
6
public override void CustomizeTypesInfo(ITypesInfo typesInfo)
{
    base.CustomizeTypesInfo(typesInfo);
    XPWeakManyToMany.CustomizeTypesInfo(typesInfo, typeof(ObjectN));
    XPWeakManyToMany.CustomizeTypesInfo(typesInfo, typeof(ObjectM));
}

XPWeakManyToMany.CustomizeTypesInfo() method adds the required metadata to correct work of the Many-To-Many relationship.

Persistent, NonPersistent

The figure below illustrates this situation.

As you can see, there is PersistentObjectM2 persistent entity and ObjectN non-persistent entity. These entities participate in an N:M (Many-To-Many) relationship.

The code snippet below shows how to implement this relationship in the application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[DomainComponent, NonPersistentDc]
public interface ObjectN2
{
    string String1 { get; set; }
    [NonPersistentDc]
    [BackReferenceProperty("ObjectN2s")]
    [XPWeakManyToMany(XPWeakManyToManyAttribute.RelationRole.Left)]
    IList ObjectM2s { get; }
}
[DomainLogic(typeof(ObjectN2))]
public class ObjectN2Logic : DomainLogicBase
{
    public ObjectN2Logic(ObjectN2 instance): base(instance){	}
    public IList ObjectM2s
    {
        get { return this.GetWeakList(x => x.ObjectM2s); }
    }
}
[DomainComponent]
public interface PersistentObjectN2 : ObjectN2 {	}
[DomainComponent]
public interface PersistentObjectM2
{
    string String1 { get; set; }
    [NonPersistentDc]
    [BackReferenceProperty("ObjectM2s")]
    [XPWeakManyToMany(XPWeakManyToManyAttribute.RelationRole.Right)]
    IList ObjectN2s { get; }
}
[DomainLogic(typeof(PersistentObjectM2))]
public class ObjectM2Logic : DomainLogicBase
{
    public ObjectM2Logic(PersistentObjectM2 instance): base(instance){}
    public IList ObjectN2s
    {
        get { return this.GetWeakList(x => x.ObjectN2s); }
    }
}

As you can see, we suggest using a special intermediate persistent class to implement the N: M relationship between PersistentObjectM2 and ObjectN2. This class exposes the non-typed references (weak reference) to persistent descendants. There are UI limitations when editing these collections.

As in the case of NonPersistent-NonPersistent, you can use a separate table to store relation objects.

You also need to update the metadata by adding the following code to the module:

1
2
3
4
5
6
public override void CustomizeTypesInfo(ITypesInfo typesInfo)
{
    base.CustomizeTypesInfo(typesInfo);
    XPWeakManyToMany.CustomizeTypesInfo(typesInfo, typeof(ObjectN2));
    XPWeakManyToMany.CustomizeTypesInfo(typesInfo, typeof(PersistentObjectM2));
}

XPWeakManyToMany.CustomizeTypesInfo() method adds the required metadata to correct work of the Many-To-Many relationship.
Load NonPersistentCollections solution.

Write US