Skip to main content

Dynamic Enums - Hybrid Enum/Lookup Table System

Overview​

All enums now support a hybrid approach that combines:

  • Enums for type safety and standard values
  • Lookup Tables for extensibility and user-defined values

This allows adding new values (like new rod materials, knot types, fishing methods) without code changes or deployments.

How It Works​

Entity Structure​

Each enum property now has an optional lookup table reference:

public class Knot
{
// Enum for type safety
public KnotCategory Category { get; set; }

// Optional lookup table for custom/extended values
public int? CategoryLookupId { get; set; }
public LookupTable? CategoryLookup { get; set; }
}

Usage Patterns​

Pattern 1: Use Enum (Standard Values)​

knot.Category = KnotCategory.LineToLine; // Standard enum value
// CategoryLookupId remains null

Pattern 2: Use Lookup Table (Custom Values)​

// Create custom category
var customCategory = new LookupTable
{
Category = "KnotCategory",
Name = "Braid to Wire",
Code = "BraidToWire",
IsSystemDefined = false,
DisplayOrder = 9
};
context.LookupTables.Add(customCategory);

// Use custom category
knot.Category = KnotCategory.LineToLine; // Closest enum (for compatibility)
knot.CategoryLookupId = customCategory.Id; // Custom value

Pattern 3: Query Both​

// Get category name (enum or lookup)
var categoryName = knot.CategoryLookup != null
? knot.CategoryLookup.Name
: knot.Category.ToString();

Enums Made Dynamic​

Rod Building​

  • ✅ RodPower - New power ratings
  • ✅ RodAction - New action types
  • ✅ RodMaterial - New materials (e.g., "Carbon Nanotube", "Hybrid Composite")
  • ✅ RodComponentType - New component types
  • ✅ GuideType - New guide types
  • ✅ GuideFrameType - New frame types
  • ✅ GuideRingType - New ring materials (e.g., "Diamond", "Titanium Nitride")
  • ✅ WrapType - New wrap types
  • ✅ WrapPattern - New patterns (e.g., "Zigzag", "Herringbone")
  • ✅ FinishType - New finish types (e.g., "Nano Coating")
  • ✅ FinishGloss - New gloss levels
  • ✅ RodBuildCondition - New condition states

Line & Knots​

  • ✅ KnotCategory - New knot categories (e.g., "Braid to Wire", "Wire to Leader")
  • ✅ KnotDifficulty - New difficulty levels
  • ✅ SpoolingCondition - New condition states
  • ✅ LineSegmentPlacement - New placement types
  • ✅ ConnectionPosition - New connection positions

Fishing Methods​

  • ✅ FishingMethod - New fishing techniques (e.g., "Kayak Fishing", "Drone Fishing")

Weather & Environmental​

  • ✅ PressureTrend - New pressure trends
  • ✅ PrecipitationType - New precipitation types (e.g., "Freezing Drizzle")
  • ✅ CloudCoverType - New cloud cover types
  • ✅ TideType - New tide types
  • ✅ TideRangeType - New range types

Catch & Verification​

  • ✅ CatchCondition - New condition states
  • ✅ CatchDisposition - New disposition types
  • ✅ VerificationType - New verification types
  • ✅ VerificationStatus - New status values
  • ✅ VerificationMethod - New verification methods
  • ✅ RecordType - New record types
  • ✅ EvidenceType - New evidence types

Seeding Enum Values​

The EnumLookupSeeder automatically seeds all enum values into lookup tables:

// During database initialization
EnumLookupSeeder.SeedEnumLookups(context);

This creates:

  1. LookupTable entries for each enum value
  2. EnumToLookupMapping entries to track the relationship

Adding New Values​

Example: New Rod Material​

Without Dynamic System (Old Way):

// 1. Update enum in code
public enum RodMaterial
{
// ... existing values
CarbonNanotube // NEW - requires code change
}

// 2. Create migration
// 3. Deploy code
// 4. Run migration

With Dynamic System (New Way):

// No code changes needed!
var newMaterial = new LookupTable
{
Category = "RodMaterial",
Name = "Carbon Nanotube",
Code = "CarbonNanotube",
Description = "Ultra-lightweight carbon nanotube composite",
IsSystemDefined = false, // User-defined
DisplayOrder = 6,
IsActive = true
};
context.LookupTables.Add(newMaterial);

// Use immediately
rodBlank.Material = RodMaterial.Other; // Closest enum
rodBlank.MaterialLookupId = newMaterial.Id; // New material

Example: New Knot Category​

// Add new knot category for specialized connections
var braidToWire = new LookupTable
{
Category = "KnotCategory",
Name = "Braid to Wire",
Code = "BraidToWire",
Description = "For connecting braided line to wire leader",
IsSystemDefined = false,
DisplayOrder = 9
};
context.LookupTables.Add(braidToWire);

// Use in knot
knot.Category = KnotCategory.Joining; // Closest enum
knot.CategoryLookupId = braidToWire.Id; // Custom category

Example: New Fishing Method​

// Add new fishing method
var droneFishing = new LookupTable
{
Category = "FishingMethod",
Name = "Drone Fishing",
Code = "DroneFishing",
Description = "Using drones to deploy baits and lines",
IsSystemDefined = false,
DisplayOrder = 60
};
context.LookupTables.Add(droneFishing);

// Use in log entry
logEntry.FishingMethod = FishingMethod.Casting; // Closest enum
logEntry.FishingMethodLookupId = droneFishing.Id; // Custom method

Querying Dynamic Values​

Get All Values (Enum + Custom)​

// Get all rod materials (system + custom)
var allMaterials = context.LookupTables
.Where(lt => lt.Category == "RodMaterial" && lt.IsActive)
.OrderBy(lt => lt.DisplayOrder)
.ToList();

// Or use helper
var materials = EnumLookupSeeder.GetLookupsForEnumType<RodMaterial>(context);

Get Lookup for Enum Value​

// Get lookup table entry for enum value
var graphiteLookup = EnumLookupSeeder.GetLookupForEnum(context, RodMaterial.Graphite);

Display Value​

// Helper method to get display name
public static string GetDisplayName<T>(T enumValue, LookupTable? lookup) where T : Enum
{
return lookup != null ? lookup.Name : enumValue.ToString();
}

// Usage
var materialName = GetDisplayName(rodBlank.Material, rodBlank.MaterialLookup);

Migration Strategy​

Phase 1: Seed Existing Enums​

// Run once during initial migration
EnumLookupSeeder.SeedEnumLookups(context);

Phase 2: Use Hybrid Approach​

  • Entities use enums for standard values
  • Entities can optionally use lookup tables for custom values
  • Both work together seamlessly

Phase 3: Full Dynamic (Future)​

  • Consider migrating frequently extended enums to lookup-only
  • Keep enums for stable, well-defined categories
  • Use lookup tables for user-extensible categories

Best Practices​

  1. Use Enums for Standard Values: Keep enums for well-defined, stable categories
  2. Use Lookup Tables for Extensible Values: Use lookup tables for categories that may grow
  3. Seed Enums on Migration: Always seed enum values into lookup tables
  4. Display Lookup Over Enum: When displaying, prefer lookup name over enum name
  5. Validate Lookup Categories: Ensure lookup table category matches enum type
  6. Track Usage: Monitor which custom values are being used
  7. Consider Promoting: If custom value becomes common, consider adding to enum

Benefits​

  1. No Code Changes: Add new values without deployments
  2. User-Defined: Users can create custom categories
  3. Type Safety: Enums still provide compile-time safety
  4. Backward Compatible: Existing code using enums continues to work
  5. Future-Proof: System can evolve without breaking changes
  6. Flexible: Support both standard and custom values simultaneously

Example: Complete Workflow​

// 1. Seed enums (one-time, during migration)
EnumLookupSeeder.SeedEnumLookups(context);

// 2. User creates custom rod material
var customMaterial = new LookupTable
{
Category = "RodMaterial",
Name = "Graphene Composite",
Code = "GrapheneComposite",
IsSystemDefined = false,
DisplayOrder = 7
};
context.LookupTables.Add(customMaterial);
context.SaveChanges();

// 3. Use in rod blank
var rodBlank = new RodBlank
{
Material = RodMaterial.Other, // Closest enum
MaterialLookupId = customMaterial.Id, // Custom value
// ... other properties
};
context.RodBlanks.Add(rodBlank);
context.SaveChanges();

// 4. Query and display
var materialName = rodBlank.MaterialLookup?.Name ?? rodBlank.Material.ToString();
// Result: "Graphene Composite"