Spensara Spensara Knowledge Base & Documentation Repository

Repository Docs & Knowledge Base

Consolidated Spensara documentation repository (722 Markdown & architecture documents in ../repository)
Built Word Documents (.docx): Spensara Overview (.docx) Capability Assessment (.docx) Brink POS Ingestion Analysis (.docx) The Borg Ingestion Architecture (.docx)

Agent Tooling (71)

Automation workflows, orchestration references, and developer tooling guides.
agent-tooling/.agents/skills/efcore-data-layer/SKILL.md 6,006 bytes · Updated Oct 23, 2026 10:44 AM EDT
Raw File

name: efcore-data-layer description: This skill should be used by AI agents to assist with Entity Framework Core data layer development, including entity configuration, migrations, and database access patterns.


Entity Framework Core Data Layer Skill

Project Structure

  • DbContext: KdsAiDbContext in Kds.Ai.Data
  • Entities: Kds.Ai.Data/Entities/
  • Configurations: Kds.Ai.Data/Configurations/
  • Migrations: Kds.Ai.Data/Migrations/

Entity Configuration

Use Fluent API configuration instead of data annotations. Create separate configuration classes implementing IEntityTypeConfiguration<TEntity>:

csharp
public sealed class MonitorSpecConfiguration : IEntityTypeConfiguration<MonitorSpec>
{
    public void Configure(EntityTypeBuilder<MonitorSpec> builder)
    {
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Name).HasMaxLength(200).IsRequired();
        builder.HasIndex(x => x.RestaurantId);
        
        builder.HasMany(x => x.Runs)
            .WithOne(x => x.MonitorSpec)
            .HasForeignKey(x => x.MonitorSpecId);
    }
}

Important: Configurations are automatically discovered and applied via ApplyConfigurationsFromAssembly in KdsAiDbContext.OnModelCreating. Do NOT manually register configurations - just create the configuration class in the Configurations/ folder and it will be auto-discovered.

Entity ID Convention

Use Guid as primary key type for all entities:

csharp
public sealed class MonitorSpec
{
    public Guid Id { get; set; }
    public Guid RestaurantId { get; set; }
    public string Name { get; set; } = string.Empty;
    // ...
}

Ids are set by the application, not generated by the database.

Migration Commands

bash
# Add new migration (run from /backend/Kds.Ai.Data directory)
cd ./backend/Kds.Ai.Data && ./Add-Migration.ps1 <MigrationName>

# Migrations are automatically applied at aspire host startup

Best Practices

  • Always use fluent configuration in Configurations/ folder
  • Never use [Key], [Required], [MaxLength] or other data annotations on entities
  • Keep entities as POCOs without EF dependencies
  • Use sealed classes for entities and configurations
  • Configure indexes explicitly for foreign keys and frequently queried columns
  • Use navigation properties with explicit foreign key configuration
  • Enums are automatically converted to strings via ConfigureConventions in DbContext
  • Configuration classes are auto-discovered - do NOT manually register them in OnModelCreating
  • For explicit transactions under retrying providers, prefer ExecuteInTransactionAsync / ExecuteInTransaction on CreateExecutionStrategy() and pass input as state to avoid closures.
  • When using ExecuteInTransactionAsync, it can return TResult, so use that result for atomic bulk operations instead of mutating outer variables.

Repository Conventions

IMPORTANT: All repositories must live in the Kds.Ai.Data project, not in feature-specific projects.

  • Location: backend/Kds.Ai.Data/Repositories/
  • Namespace: Kds.Ai.Data.Repositories
  • Registration: Register all repositories in Kds.Ai.Data/DependencyInjectionExtensions.cs using the AddDataModule() method
  • Naming:
  • Interface: I{Entity}Repository (e.g., IMonitorSpecRepository, IMonitorRunRepository)
  • Implementation: Ef{Entity}Repository (e.g., EfMonitorSpecRepository, EfMonitorRunRepository)
  • Access Modifier: Implementation classes should be internal sealed (interfaces are public)

Example:

csharp
// In Kds.Ai.Data/Repositories/IMonitorRunRepository.cs
namespace Kds.Ai.Data.Repositories;

public interface IMonitorRunRepository
{
    Task<MonitorRun?> GetByIdAsync(Guid id, CancellationToken ct = default);
    // ...
}

// In Kds.Ai.Data/Repositories/EfMonitorRunRepository.cs
namespace Kds.Ai.Data.Repositories;

internal sealed class EfMonitorRunRepository(KdsAiDbContext dbContext) : IMonitorRunRepository
{
    // Implementation
}

// In Kds.Ai.Data/DependencyInjectionExtensions.cs
builder.Services.AddScoped<IMonitorRunRepository, EfMonitorRunRepository>();

Feature-specific projects (like Kds.Ai.AnomalyDetector) should only consume repositories via dependency injection, never define them.

Data Seeding Conventions

IMPORTANT: All data seeding logic must live in the Kds.Ai.Data project, not in feature-specific projects.

  • Location: backend/Kds.Ai.Data/Seeding/
  • Namespace: Kds.Ai.Data.Seeding
  • Registration: Seeding is configured in Kds.Ai.Data/DependencyInjectionExtensions.cs using EF Core's UseSeeding() and UseAsyncSeeding() callbacks
  • Naming: {Entity}Seeding for static seed classes (e.g., MonitorSpecSeeding)
  • Access Modifier: Seed classes should be public static with public static methods

Example:

csharp
// In Kds.Ai.Data/Seeding/MonitorSpecSeeding.cs
namespace Kds.Ai.Data.Seeding;

public static class MonitorSpecSeeding
{
    public static void Seed(KdsAiDbContext context)
    {
        SeedAsync(context, CancellationToken.None).GetAwaiter().GetResult();
    }

    public static async Task SeedAsync(KdsAiDbContext context, CancellationToken cancellationToken)
    {
        // Seeding logic
    }
}

// In Kds.Ai.Data/DependencyInjectionExtensions.cs
builder.Services.AddDbContext<KdsAiDbContext>(options =>
{
    options
        .UseSeeding((context, _) =>
        {
            if (context is KdsAiDbContext db)
                MonitorSpecSeeding.Seed(db);
        })
        .UseAsyncSeeding(async (context, _, cancellationToken) =>
        {
            if (context is KdsAiDbContext db)
                await MonitorSpecSeeding.SeedAsync(db, cancellationToken);
        });
});

Feature-specific projects should never define seeding logic. If you need seed data for a specific feature, add it to the appropriate seeding class in Kds.Ai.Data/Seeding/.