To Define a Foreign Key
You can define a foreign key based on the primary key of the referenced data access class (DAC) and select records by using this key, as described in this topic.
To Define a Foreign Key and Use It to Select Records
- In the DAC of the referenced table, define the primary key, as described in
To Define a Primary Key. The following code shows an example of the definition of the primary key
that is used in the other code examples in this
topic.
public partial class SOOrder : PX.Data.PXBqlTable, PX.Data.IBqlTable { public class PK : PrimaryKeyOf<SOOrder>.By<orderType, orderNbr> { public static SOOrder Find( PXGraph graph, string orderType, string orderNbr) => FindBy(graph, orderType, orderNbr); } public abstract class orderType : PX.Data.BQL.BqlString.Field<orderType> { } public abstract class orderNbr : PX.Data.BQL.BqlString.Field<orderNbr> { } }
- In the child DAC, define the foreign key based on the primary key of the parent
table, as shown
below.
public partial class SOLine : PX.Data.PXBqlTable, PX.Data.IBqlTable { public static class FK { public class Order : SOOrder.PK.ForeignKeyOf<SOLine>.By<orderType, orderNbr> { } } public abstract class orderType : PX.Data.BQL.BqlString.Field<orderType> { } public abstract class orderNbr : PX.Data.BQL.BqlString.Field<orderNbr> { } }
- Use the foreign key to select the parent record or child records, as shown in
the following
code.
//Select the parent record SOOrder order = SOLine.FK.FindParent(this, soLine); //Select the child records IEnumerable<SOLine> lines = SOLine.FK.SelectChildren(this, soOrder);
Examples of Usage
Static foreign keys should be used for configuring the PXParentAttribute and PXForeignReferenceAttribute attributes to reduce semantic duplication of these elements as shown in the following code.
public partial class SOLine : PX.Data.PXBqlTable, PX.Data.IBqlTable
{
public static class FK
{
public class SOOrder : PX.Objects.SO.SOOrder.PK.ForeignKeyOf<SOLine>.By<orderType, orderNbr> { }
public class Inventory : PX.Objects.IN.InventoryItem.PK.ForeignKeyOf<SOLine>.By<inventoryID> { }
}
public abstract class orderType : PX.Data.BQL.BqlString.Field<orderType> { }
[...]
[PXParent(typeof(FK.SOOrder))]
public virtual String OrderNbr { get; set; }
public abstract class orderNbr : PX.Data.BQL.BqlString.Field<orderNbr> { }
[...]
[PXForeignReference(typeof(FK.Inventory))]
public virtual Int32? InventoryID { get; set; }
public abstract class inventoryID : PX.Data.BQL.BqlInt.Field<inventoryID> { }
}
Also, you can use a foreign key as a condition in the JOIN statement, as shown in the following code.
var rows =
SelectFrom<SOOrder>.
InnerJoin<SOLine>.On<SOLine.FK.SOOrder>.
// On<
// SOLine.orderType.IsEqual<SOOrder.orderType>.
// And<SOLine.orderNbr.IsEqual<SOOrder.orderNbr>>>
InnerJoin<SOLineSplit>.On<SOLineSplit.FK.SOLine>.
// On<
// SOLineSplit.orderType.IsEqual<SOLine.orderType>.
// And<SOLineSplit.orderNbr.IsEqual<SOLine.orderNbr>>.
// And<SOLineSplit.lineNbr.IsEqual<SOLine.lineNbr>>>
InnerJoin<InventoryItem>.On<SOLine.FK.Inventory>.
// On<SOLine.inventoryID.IsEqual<InventoryItem.inventoryID>>
Where<
SOOrder.orderType.IsEqual<@P.AsString>.
And<SOOrder.orderNbr.IsEqual<@P.AsString>>>.
View.Select(this, "SO", "SO000001");
You can use the same practice for virtual JOIN statements that are based on
references defined through the Current
parameter in a graph
view.
public class MyGraph : PXGraph<MyGraph>
{
public SelectFrom<SOOrder>.View orders;
public SelectFrom<SOLine>.Where<SOLine.FK.SOOrder.SameAsCurrent>.View lines;
// public
// SelectFrom<SOLine>.
// Where<
// SOLine.orderType.IsEqual<SOOrder.orderType.FromCurrent>.
// And<SOLine.orderNbr.IsEqual<SOOrder.orderNbr.FromCurrent>>>.
// View lines;
public SelectFrom<SOLineSplit>.Where<SOLineSplit.FK.SOLine.SameAsCurrent>.View splits;
// public
// PXSelect<SOLineSplit>.
// Where<
// SOLineSplit.orderType.IsEqual<SOLine.orderType.FromCurrent>.
// And<SOLineSplit.orderNbr.IsEqual<SOLine.orderNbr.FromCurrent>>.
// And<SOLineSplit.lineNbr.IsEqual<SOLine.lineNbr.FromCurrent>>>.
// View splits;
}
Coding Conventions
We recommend the following coding conventions, which are checked by Acuminator:
- All foreign keys can have arbitrary names, but they must be declared in the
public static class named
FK
. - To declare multiple foreign keys, you should aggregate all foreign keys of a DAC
inside a single
FK
class, so that their usage looks more like an operation application, for example,SOLine.FK.SOOrder
. Also, this approach you not to merge theFK
classes with the fields of the DAC, which makes it easier to find the proper class via IntelliSence. - Multiple foreign keys declared in a DAC should not use the same set of fields.
- You cannot use unbound DAC fields in a foreign key declaration. For details, see Restrictions.