Issues related to communication between non-persistent domain components are described in this article.
The use of domain components imposes certain limitations on the description of 1:N relations between them. In other words, by default, XAF does not permit to use non-persistent entities in the 1:N relation. See more detailed information on the implementation of relations at
From the angle of persistence, there are 4 possible combinations of relations:
Persistent Master | Non-persistent Master | |
---|---|---|
Persistent Details | + | + |
Non-persistent Details | - | + |
Persistent Master, persistent Details
It is a standard combination, we will not discuss it here.
Persistent Master, non-persistent Details
I was unable to imagine a situation in which this option could be useful in practice. Therefore, we will not discuss this combination, either.
Non-persistent Master, persistent Details
An example for this case is given below.
There are entities, Master1 and Details1. Master1 contains a calculated non-persistent collection of objects Details1, and Details1 contains non-type-safe link
1 | Console.WrightLine(); |
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 | [DomainComponent, NonPersistentDc] public interface Master1 { [NonPersistentDc, DevExpress.ExpressApp.DC.Aggregated, BackReferenceProperty("Master1")] IList Details1 { get; } } [DomainLogic(typeof(Master1))] public class Master1Logic : DomainLogicBase { public Master1Logic(Master1 instance): base(instance) {} public IList Details1 { get { return this.GetWeakList("Details1"); } } } [DomainComponent] public interface Details1 { [Browsable(false), DevExpress.ExpressApp.DC.Aggregated] XPWeakReference Master1 { get; set; } } [DefaultClassOptions, DomainComponent] public interface DerivedMaster1 : Master1 { } [DefaultClassOptions, DomainComponent] public interface OneMoreDerivedMaster1 : Master1 { } |
When implementing domain logic Master1Logic, base class DomainLogicBase<> is used; it implements method GetWeakList<>(), which is necessary in this case.
That both heirs of Master1 have fully valid persistent collections Details1 with absolutely identical behavior, is an advantage of this approach. In so doing, we did not need to duplicate the code.
Note
A separate table is created for XPWeakRefrence in the Database, and, if there are a few million such links in your application, all of them will be stored in this table. It may have a negative effect on the performance of the application as a whole.
Instead of DevExpress.Xpo.XPWeakReference, you can use structure XPWeakReferenceStruct, which is implemented in Xafari. Unlike XPWeakReference, this structure keeps its data in the same table where the persistent class keeps its data (
DomainLogicBase<> processes both types of non-type-safe links.
Non-persistent Master, non-persistent Details
An example for this case is described below.
There are two entities, Master2 and Master2Details1. Master2 contains a calculated non-persistent collection of objects Master2Details1, while Master2Details1 contains a link to Master2, which is similar to BackRefrence. DerivedMaster2 and OneMoreDerivedMaster2 are persistent heirs of Master2. DerivedMaster2Details1 and OneMoreDerivedMaster2Details1 are persistent heirs of Master2Details1.
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 46 47 48 49 50 51 52 53 54 55 | [DomainComponent, NonPersistentDc] public interface Master2 { [NonPersistentDc, Browsable(false)] IList Details1 { get; } } [DomainLogic(typeof(Master2))] public class Master2Logic : DomainLogicBase { public Master2Logic(Master2 instance) : base(instance) { } public IList Details1 { get { return this.Instance.GetListValueByTerm("Details1"); } } } [DomainComponent, DefaultClassOptions] public interface DerivedMaster2 : Master2 { [BackReferenceProperty("DerivedMaster2"), Aggregated, Term("Details1")] IList DerivedDetails1 { get; } } [DomainComponent, DefaultClassOptions] public interface OneMoreDerivedMaster2 : Master2 { [BackReferenceProperty("OneMoreDerivedMaster2"), Aggregated, Term("Details1")] IList OneMoreDerivedDetails1 { get; } } [DomainComponent, NonPersistentDc] public interface Master2Details1 { [Browsable(false)] Master2 Master2 { get; set; } } [DomainLogic(typeof(Master2Details1))] public class Master2Details1Logic : DomainLogicBase { public Master2Details1Logic(Master2Details1 instance) : base(instance) {} public Master2 Master2 { get { return this.Instance.GetValueByTerm("Master2"); } set { this.Instance.SetValueByTerm("Master2", value);} } } [DomainComponent] public interface DerivedMaster2Details1 : Master2Details1 { [Term("Master2")] DerivedMaster2 DerivedMaster2 { get; set; } } [DomainComponent] public interface OneMoreDerivedMaster2Details1 : Master2Details1 { [Term("Master2")] OneMoreDerivedMaster2 OneMoreDerivedMaster2 { get; set; } } |
Here we use an approach where the calculated properties for the collection and the back reference are described at the non-persistent level. However, the real relations between the entities only come to existence in persistent heirs. In so doing, the persistent properties must be marked with attribute Term. This allows determining exactly the properties that implement the relations, in the base logic.
The following extension methods for Type are used in this example:
- GetValueByTerm()
- SetValueByTerm()
- GetListValueByTerm()
These methods allow controlling property values of the entity through Term names, instead of using property names. It is convenient when property names are not yet known, but the Term names are known, at the time of writing the code.
This method was used for describing hierarchical dictionaries HierarchicalClassifierItem using IHiererchyNode.