This document describes the PX1036 diagnostic.
| Code | Short Description | Type | Code Fix |
|---|---|---|---|
| PX1036 | The DAC must have one primary key, which should be named PK. The class containing DAC foreign keys should be named FK. The single unique key in the DAC should be named UK. If the DAC has multiple unique keys, they should be declared in a public static class named UK. |
Warning (ISV Level 3: Informational) | Available |
The diagnostic checks the correct naming of primary, foreign, and unique key field declarations in DACs. For a primary key, the name should be PK. Dirty primary keys are not considered for renaming by the diagnostic.
Dirty primary and unique keys are keys derived from the PrimaryKeyOf<TTable>.By<TKeyComponent>.Dirty type that allow developers to find dirty records.
Here is an example of the primary key:
public class PK_Dirty : PrimaryKeyOf<INUnit>.By<recordID>.Dirty
{
public static INUnit Find(PXGraph graph, long? recordID) => FindBy(graph, recordID);
}For a foreign key, the name can be arbitrary but the key should be declared in a public static class called FK. For a single unique key, the name should be UK.
If a DAC has multiple unique keys, they can have arbitrary names but they must be declared in a public static class called UK.
The warning is shown only for DACs that have an attribute derived from the PXCacheName attribute, or an attribute derived from the PXPrimaryGraphBase attribute such as PXPrimaryGraphAttribute.
The diagnostic does not check abstract DACs or fully unbound DACs (also called virtual DACs) which are used for custom pop-ups and filters on the inquiry forms. Such DACs do not require primary, foreign, or unique key declarations. Since Acuminator does not access the database, it determines whether a DAC is fully unbound based on the following rules:
- A DAC without any DAC fields or with only unbound DAC fields is considered fully unbound.
- A DAC marked with
PX.Data.PXVirtualAttributeis always considered fully unbound even if it has DB bound fields. - A DAC marked with
PX.Data.PXProjectionAttribute,PX.Data.PXAccumulatorAttributeor any other attribute derived fromPX.Data.PXDBInterceptorAttributeis always considered DB bound even if it has only unbound fields. - A DAC with a DB bound
NoteIDfield is considered fully unbound ifNoteIDis the only DB bound field in the DAC and all other fields in the DAC are unbound.
The diagnostic also checks the declaration and implementation of a unique key by using the following rules:
- A single unique key in a DAC declaration must have the name
UK. - Multiple unique keys can have arbitrary names but they must be declared inside a public static class named
UK. - The primary key differs from unique keys only by two things: Its name is
PK, and its set of fields consists of all DAC key properties (DAC properties marked withIsKey = true). The DAC should not have any unique key declarations without a primary key declaration. Acuminator searches for an appropriate candidate for a primary key among the DAC unique keys. It looks for a unique key whose set of fields is composed of all DAC key properties.- If there is a suitable unique key in the DAC, Acuminator suggests that you turn it into a primary key named
PKby renaming it and optionally changing its location to make it a first DAC's type member. This logic is applied for unique keys that are not direct DAC type members because they are declared in the nestedUKcontainer class. - If there is no appropriate unique key, Acuminator shows an error for the PX1033 diagnostic indicating the missing primary key.
- If there is a suitable unique key in the DAC, Acuminator suggests that you turn it into a primary key named
To fix the issue, change the name of the primary key class to PK, make sure that all foreign key classes are declared in a public static class called FK, and declare the unique keys as described in the rules above.
The code fix suggests that you do the following:
- Change the name of a suitable unique key class to
PKif there is no primary key class declaration in the DAC - Place all foreign keys in a public static class named
FK - Change the name of the single unique key to
UK - Place all unique keys in a public static class named
UK
For foreign keys, the diagnostic searches for a public static class named FK. There could be three possible outcomes:
- The
FKclass is not found. Then the diagnostic suggests to generate it and move all DAC foreign keys there. - The
FKclass is found and it is public and static. The diagnostic suggests to move all DAC foreign key declarations which are not already inside theFKclass into the found class. - The
FKclass is found but it is not declared correctly (not public or static). In this case, the diagnostic does not suggest a code fix.
An example of an incorrectly named primary key is shown in the following code.
[PXCacheName("Sales Order")]
public class SOOrder : PXBqlTable, IBqlTable
{
public class IncorrectPKName : PrimaryKeyOf<SOOrder>.By<orderType, orderNbr>
{
public static SOOrder Find(PXGraph graph, string orderType, string orderNbr) => FindBy(graph, orderType, orderNbr);
}
[PXDBString(IsKey = true, InputMask = "")]
[PXDefault]
[PXUIField(DisplayName = "Order Type")]
public string OrderType { get; set; }
public abstract class orderType : IBqlField { }
[PXDBString(IsKey = true, InputMask = "")]
[PXDefault]
[PXUIField(DisplayName = "Order Nbr.")]
public string OrderNbr { get; set; }
public abstract class orderNbr : IBqlField { }
[PXStringList(new[] { "N", "O" }, new[] { "New", "Open" })]
[PXDBString]
[PXUIField(DisplayName = "Status")]
public string Status { get; set; }
public abstract class status : IBqlField { }
[PXDBTimestamp]
public virtual byte[] tstamp { get; set; }
public abstract class Tstamp : IBqlField { }
}An example of a corrected primary key is shown in the following code.
[PXCacheName("SO Order")]
public class SOOrder : PXBqlTable, IBqlTable
{
public class PK : PrimaryKeyOf<SOOrder>.By<orderType, orderNbr>
{
public static SOOrder Find(PXGraph graph, string orderType, string orderNbr) => FindBy(graph, orderType, orderNbr);
}
[PXDBString(IsKey = true, InputMask = "")]
[PXDefault]
[PXUIField(DisplayName = "Order Type")]
public string OrderType { get; set; }
public abstract class orderType : IBqlField { }
[PXDBString(IsKey = true, InputMask = "")]
[PXDefault]
[PXUIField(DisplayName = "Order Nbr.")]
public string OrderNbr { get; set; }
public abstract class orderNbr : IBqlField { }
[PXStringList(new[] { "N", "O" }, new[] { "New", "Open" })]
[PXDBString]
[PXUIField(DisplayName = "Status")]
public string Status { get; set; }
public abstract class status : IBqlField { }
[PXDBTimestamp]
public virtual byte[] tstamp { get; set; }
public abstract class Tstamp : IBqlField { }
}An example of multiple unique key declarations without a primary key declaration is shown in the following code.
[PXCacheName("INUnit")]
public partial class INUnitMultipleUniqueKeysNoPK : PXBqlTable, IBqlTable
{
public static class UK
{
public class ByAll : PrimaryKeyOf<INUnitMultipleUniqueKeysNoPK>.By<unitType, fromUnit, toUnit, inventoryID, itemClassID>
{
public static INUnitMultipleUniqueKeysNoPK Find(PXGraph graph, short? unitType, string fromUnit, string toUnit, int? inventoryID, int? itemClassID) =>
FindBy(graph, unitType, fromUnit, toUnit, inventoryID, itemClassID);
}
public class ByGlobal : PrimaryKeyOf<INUnitMultipleUniqueKeysNoPK>.By<unitType, fromUnit, toUnit>
{
public static INUnitMultipleUniqueKeysNoPK Find(PXGraph graph, string fromUnit, string toUnit) => FindBy(graph, INUnitType.Global, fromUnit, toUnit);
}
public class ByInventory : PrimaryKeyOf<INUnitMultipleUniqueKeysNoPK>.By<unitType, inventoryID, fromUnit>
{
public static INUnitMultipleUniqueKeysNoPK Find(PXGraph graph, int? inventoryID, string fromUnit) => FindBy(graph, INUnitType.InventoryItem, inventoryID, fromUnit);
}
public class ByItemClass : PrimaryKeyOf<INUnitMultipleUniqueKeysNoPK>.By<unitType, itemClassID, fromUnit>
{
public static INUnitMultipleUniqueKeysNoPK Find(PXGraph graph, int? itemClassID, string fromUnit) => FindBy(graph, INUnitType.ItemClass, itemClassID, fromUnit);
}
}
...
}An example of multiple unique key declarations where one declaration was turned into a primary key declaration is shown in the following code.
[PXCacheName("INUnit")]
public partial class INUnitMultipleUniqueKeysNoPK : PXBqlTable, IBqlTable
{
public class PK : PrimaryKeyOf<INUnitMultipleUniqueKeysNoPK>.By<unitType, fromUnit, toUnit, inventoryID, itemClassID>
{
public static INUnitMultipleUniqueKeysNoPK Find(PXGraph graph, short? unitType, string fromUnit, string toUnit, int? inventoryID, int? itemClassID) =>
FindBy(graph, unitType, fromUnit, toUnit, inventoryID, itemClassID);
}
public static class UK
{
public class ByGlobal : PrimaryKeyOf<INUnitMultipleUniqueKeysNoPK>.By<unitType, fromUnit, toUnit>
{
public static INUnitMultipleUniqueKeysNoPK Find(PXGraph graph, string fromUnit, string toUnit) => FindBy(graph, INUnitType.Global, fromUnit, toUnit);
}
public class ByInventory : PrimaryKeyOf<INUnitMultipleUniqueKeysNoPK>.By<unitType, inventoryID, fromUnit>
{
public static INUnitMultipleUniqueKeysNoPK Find(PXGraph graph, int? inventoryID, string fromUnit) => FindBy(graph, INUnitType.InventoryItem, inventoryID, fromUnit);
}
public class ByItemClass : PrimaryKeyOf<INUnitMultipleUniqueKeysNoPK>.By<unitType, itemClassID, fromUnit>
{
public static INUnitMultipleUniqueKeysNoPK Find(PXGraph graph, int? itemClassID, string fromUnit) => FindBy(graph, INUnitType.ItemClass, itemClassID, fromUnit);
}
}
...
}