Documentation
reference/patterns/entity-constants.md
Entity Constant Patterns
Status: Active
Enforced By: Roslyn Analyzers ACSIS0019-ACSIS0022
Last Updated: 2025-01-04
Overview
Dynaplex enforces consistent entity constant conventions for all database entity classes. These constants provide type-safe references to table names and platform type identifiers, preventing magic strings and enabling better refactoring support.
Required Constants
TABLE_NAME Constant (ACSIS0019, ACSIS0020)
All entity classes with a [Table] attribute must define a TABLE_NAME constant:
[Table(TABLE_NAME)]
public class Item
{
public const string TABLE_NAME = "items";
// ... entity properties
}
Rules:
- Must be
public const string TABLE_NAME - Value must match PostgreSQL naming convention (lowercase, snake_case)
- The
[Table]attribute must reference the constant, not a string literal - ❌ Wrong:
[Table("items")](literal string) - ✅ Correct:
[Table(TABLE_NAME)](constant reference)
Benefits:
- Single source of truth for table names
- Refactoring support (rename constant updates all references)
- Prevents typos in table name strings
- Enables compile-time verification
PTID Constant (ACSIS0021, ACSIS0022)
Passport-enabled entities (entities with PlatformTypeId property) must define a PTID constant:
[Table(TABLE_NAME)]
public class Item
{
public const string TABLE_NAME = "items";
public const short PTID = PTIDS.ITEM;
[Key]
[Column("id")]
public Guid Id { get; set; }
[Column("platform_type_id")]
public short PlatformTypeId { get; set; } = PTID;
// ... other properties
}
Rules:
- Must be
public const short PTID - Value must be a reference to
PTIDS.CONSTANT_NAMEfrom CoreData.Abstractions - The
PlatformTypeIdproperty default value must use thePTIDconstant - ❌ Wrong:
PlatformTypeId { get; set; } = 801;(hardcoded literal) - ✅ Correct:
PlatformTypeId { get; set; } = PTID;(constant reference)
Benefits:
- Type-safe platform type identifier references
- Prevents hardcoded "magic numbers"
- Ensures consistency with PTIDS registry
- Enables compile-time verification of platform types
Analyzer Rules
ACSIS0019: Entity missing TABLE_NAME constant
Severity: Warning
Trigger: Entity class has [Table] attribute but no TABLE_NAME constant
Example Violation:
[Table("items")] // Has Table attribute
public class Item // ❌ Missing TABLE_NAME constant
{
// ... properties
}
Fix:
[Table(TABLE_NAME)]
public class Item
{
public const string TABLE_NAME = "items"; // ✅ Added constant
// ... properties
}
Code Fix: Auto-generates TABLE_NAME constant from [Table] attribute value
ACSIS0020: [Table] attribute should use TABLE_NAME constant
Severity: Info (auto-fixable)
Trigger: Entity has TABLE_NAME constant but [Table] uses string literal
Example Violation:
[Table("items")] // ❌ Using literal string
public class Item
{
public const string TABLE_NAME = "items";
// ... properties
}
Fix:
[Table(TABLE_NAME)] // ✅ Using constant
public class Item
{
public const string TABLE_NAME = "items";
// ... properties
}
Code Fix: Automatically replaces literal with constant reference
ACSIS0021: Passport-enabled entity missing PTID constant
Severity: Warning
Trigger: Entity has PlatformTypeId property but no PTID constant
Example Violation:
[Table(TABLE_NAME)]
public class Item // ❌ Missing PTID constant
{
public const string TABLE_NAME = "items";
public short PlatformTypeId { get; set; } = 303;
}
Fix:
[Table(TABLE_NAME)]
public class Item
{
public const string TABLE_NAME = "items";
public const short PTID = PTIDS.ITEM; // ✅ Added PTID constant
public short PlatformTypeId { get; set; } = PTID;
}
Code Fix: Auto-generates PTID constant (may need manual PTIDS value lookup)
ACSIS0022: PlatformTypeId should use PTID constant
Severity: Info (auto-fixable)
Trigger: Entity has PTID constant but PlatformTypeId uses hardcoded literal
Example Violation:
[Table(TABLE_NAME)]
public class Item
{
public const string TABLE_NAME = "items";
public const short PTID = PTIDS.ITEM;
public short PlatformTypeId { get; set; } = 303; // ❌ Hardcoded literal
}
Fix:
[Table(TABLE_NAME)]
public class Item
{
public const string TABLE_NAME = "items";
public const short PTID = PTIDS.ITEM;
public short PlatformTypeId { get; set; } = PTID; // ✅ Using constant
}
Code Fix: Automatically replaces literal with PTID constant
Entity Classification
1. Passport-Enabled Entities
Entities that integrate with the Prism Passport system.
Characteristics:
- Primary key:
Guid Id(not auto-generated) - Has
short PlatformTypeIdproperty - FK relationship to
prism.passports - Registered in
PTIDSregistry
Required Constants:
- ✅
TABLE_NAME - ✅
PTID
Example:
using Acsis.Dynaplex.Engines.CoreData.Abstractions.Primitives;
[Table(TABLE_NAME)]
public class Item
{
public const string TABLE_NAME = "items";
public const short PTID = PTIDS.ITEM;
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column("id")]
public Guid Id { get; set; }
[Column("platform_type_id")]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public short PlatformTypeId { get; set; } = PTID;
// ... other properties
}
2. Reference Data Entities
Simple lookup tables or reference data without passport integration.
Characteristics:
- Primary key:
intorshort(often auto-generated) - No
PlatformTypeIdproperty - No Passport integration
Required Constants:
- ✅
TABLE_NAME - ❌
PTID(not applicable)
Example:
[Table(TABLE_NAME)]
public class UserStatus
{
public const string TABLE_NAME = "user_statuses";
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("id")]
public int Id { get; set; }
[Column("description")]
public string Description { get; set; } = null!;
}
3. Excluded Entity Types
Some entity types are excluded from these analyzer rules:
DbContext Classes
- Reason: Not entity classes
- DbContexts have their own constants (e.g.,
SCHEMA)
Journal/Log Classes
- Pattern: Class name ends with "Log"
- Reason: Follow different patterns (inherit from
JournalRowBase)
Legacy IoT Entities
- Pattern: Class name starts with "Tn" or "Zb"
- Reason: External system integrations with different conventions
PlatformType Entity
- Reason: This IS the platform type registry itself
PTIDS Registry
The PTIDS class in CoreData.Abstractions defines all platform type identifiers:
// engines/core-data/src/Acsis.Dynaplex.Engines.CoreData.Abstractions/Primitives/PTIDS.cs
public static class PTIDS
{
// Core Data: 0-99
public const short UNIT_OF_MEASURE = 30;
// System Environment: 100-199
public const short ORGANIZATION = 110;
// Identity: 200-299
public const short TENANT = 201;
public const short USER = 203;
// Catalog: 300-399
public const short ITEM_CATEGORY = 301;
public const short ITEM_TYPE = 302;
public const short ITEM = 303;
// Spatial: 400-499
public const short LOCATION = 403;
// Workflow: 600-699
public const short WORKFLOW = 602;
// Printing: 800-899
public const short PRINTER = 801;
// Prism: 1000-1100
public const short PASSPORT = 1001;
// Events: 5000-5100
public const short EVENT = 5001;
}
PTID Allocation:
- Each component has a reserved range (e.g., Identity: 200-299)
- New PTIDs must be added to the
PTIDSclass first - Namespace prevents conflicts across components
Code Fix Providers
All four analyzer rules have code fix providers that can automatically remediate violations:
Auto-Fix Capabilities
Add TABLE_NAME constant (ACSIS0019)
- Extracts table name from
[Table]attribute - Generates
public const string TABLE_NAME = "value"; - Inserts at top of class
- Extracts table name from
Replace [Table] literal with constant (ACSIS0020)
- Changes
[Table("literal")]→[Table(TABLE_NAME)] - Preserves whitespace and formatting
- Changes
Add PTID constant (ACSIS0021)
- Extracts PTID value from
PlatformTypeIdproperty initializer - Generates
public const short PTID = PTIDS.CONSTANT_NAME; - Inserts after
TABLE_NAMEconstant - Note: May require manual adjustment of
PTIDS.CONSTANT_NAMEvalue
- Extracts PTID value from
Replace PlatformTypeId literal with PTID (ACSIS0022)
- Changes
= 801→= PTID - Preserves property structure
- Changes
Using Code Fixes in Visual Studio / Rider
- Place cursor on warning/error squiggle
- Press
Ctrl+.(Windows/Linux) orCmd+.(Mac) - Select "Add TABLE_NAME constant" or appropriate fix
- Code is automatically updated
Batch Fixing
Use "Fix All" in Visual Studio to apply code fixes across:
- Document: Current file only
- Project: Entire project
- Solution: All projects
Migration Guide
Migrating Existing Entities
For entities that don't follow these patterns:
Step 1: Run analyzers
dotnet build
# Analyzer warnings will appear
Step 2: Apply code fixes
- In IDE: Use Quick Actions (
Ctrl+./Cmd+.) - Batch: Use "Fix All in Project" or "Fix All in Solution"
Step 3: Verify PTID values
- Ensure
PTIDconstants reference correctPTIDS.CONSTANT_NAME - Add new PTIDs to
PTIDSregistry if needed
Step 4: Update [Table] attributes
- Replace all
[Table("literal")]with[Table(TABLE_NAME)]
Step 5: Build and test
dotnet build
# Should have no ACSIS0019-0022 warnings
Adding New Entities
When creating a new entity class:
1. Define constants first:
[Table(TABLE_NAME)]
public class MyNewEntity
{
public const string TABLE_NAME = "my_new_entities";
public const short PTID = PTIDS.MY_NEW_ENTITY; // Add to PTIDS if needed
2. Add to PTIDS registry (if passport-enabled):
// In CoreData.Abstractions/Primitives/PTIDS.cs
public static class PTIDS
{
// ... existing constants
public const short MY_NEW_ENTITY = 999; // Use appropriate range
}
3. Use constants in properties:
[Column("platform_type_id")]
public short PlatformTypeId { get; set; } = PTID;
Best Practices
✅ DO
- Define constants at the top of the class (after class declaration)
- Use
TABLE_NAMEin[Table]attribute - Use
PTIDforPlatformTypeIddefault value - Add new PTIDs to
PTIDSregistry before using - Follow PostgreSQL naming: lowercase, snake_case, plural for tables
❌ DON'T
- Use string literals in
[Table]attributes - Hardcode platform type ID numbers
- Create PTIDs without adding to registry
- Use different constant names (e.g.,
TABLE,TABLENAME,PLATFORM_TYPE_ID)
Related Documentation
- Database Architecture Pattern - Schema isolation and cross-component references
- Passport Pattern - Global ID registry system
- Journal Tables - Audit log entities
- ADR-036: Database Project Separation
Troubleshooting
"Missing PTID constant" but entity shouldn't have one
Solution: Your entity may not need passport integration.
If the entity has int or short primary key (not Guid), it's reference data and doesn't need PTID. The analyzer only triggers for entities with PlatformTypeId property.
Code fix generates wrong PTID value
Solution: Manually correct the PTIDS constant name.
The code fix provider guesses the PTID constant name from the class name. Verify it matches an existing value in the PTIDS registry:
// Code fix may generate:
public const short PTID = PTIDS.MY_ENTITY;
// Verify PTIDS.MY_ENTITY exists in CoreData.Abstractions/Primitives/PTIDS.cs
Analyzer not running in IDE
Solution: Rebuild the analyzer project.
dotnet build strata/analyzers/src/Acsis.RoslynAnalyzers/
Restart your IDE if analyzer still doesn't appear.
Examples
Exemplary Entity: Item.cs
✅ Perfect implementation of all patterns:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Acsis.Dynaplex.Engines.CoreData.Abstractions.Primitives;
namespace Acsis.Dynaplex.Engines.Catalog.Database;
[Table(TABLE_NAME)]
public class Item
{
public const string TABLE_NAME = "items";
public const short PTID = PTIDS.ITEM;
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column("id", Order = 0)]
public Guid Id { get; set; }
[Column("platform_type_id", Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public short PlatformTypeId { get; set; } = PTID;
[Required]
[Column("name", Order = 2)]
[StringLength(256)]
public string Name { get; set; } = null!;
[Column("tenant_id", Order = 99)]
public Guid TenantId { get; set; }
}
Reference Entity: UserStatus.cs
✅ Correct implementation for non-passport entity:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Acsis.Dynaplex.Engines.Identity.Database;
[Table(TABLE_NAME)]
public class UserStatus
{
public const string TABLE_NAME = "user_statuses";
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("id")]
public int Id { get; set; }
[Column("description")]
[StringLength(50)]
public string Description { get; set; } = null!;
}
Summary
Entity constant patterns ensure:
- Consistency: All entities follow the same conventions
- Maintainability: Constants enable safe refactoring
- Type Safety: Compile-time verification prevents runtime errors
- Clarity: No magic strings or numbers in code
The Roslyn analyzers (ACSIS0019-0022) automatically enforce these patterns with helpful code fixes to guide developers toward correct implementations.