> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aspfox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Adding a Database Entity

> Quick reference for the entity → configuration → migration → DbSet pattern.

A condensed reference for adding a new EF Core entity. For the full walkthrough including commands, queries, and frontend, see [Adding a Feature](/guides/adding-a-feature).

## Checklist

<Steps>
  <Step title="Create the entity class in Domain">
    ```csharp theme={null}
    // src/Acme.Domain/Entities/Tag.cs
    public class Tag : BaseEntity, ITenantScopedEntity, IAuditableEntity
    {
        public string Name { get; private set; } = string.Empty;
        public string Color { get; private set; } = "#6B7280";
        public Guid TenantId { get; private set; }
        public Tenant Tenant { get; private set; } = null!;
        public DateTime CreatedAt { get; set; }
        public DateTime? UpdatedAt { get; set; }

        private Tag() { }

        public static Tag Create(string name, string color, Guid tenantId)
        {
            return new Tag { Id = Guid.NewGuid(), Name = name, Color = color, TenantId = tenantId };
        }
    }
    ```

    **Interfaces to implement:**

    * `BaseEntity` — provides `Id` (Guid)
    * `ITenantScopedEntity` — requires `TenantId` property; enables global query filter
    * `IAuditableEntity` — requires `CreatedAt` and `UpdatedAt`; automatically set by `SaveChangesAsync`

    **Not tenant-scoped?** If the entity belongs to a user rather than a tenant (e.g., user profile data), implement `IUserScopedEntity` instead and filter by `UserId`.
  </Step>

  <Step title="Create the EF Core configuration">
    ```csharp theme={null}
    // src/Acme.Infrastructure/Persistence/Configurations/TagConfiguration.cs
    public class TagConfiguration : IEntityTypeConfiguration<Tag>
    {
        private readonly TenantContext _tenantContext;

        public TagConfiguration(TenantContext tenantContext)
        {
            _tenantContext = tenantContext;
        }

        public void Configure(EntityTypeBuilder<Tag> builder)
        {
            builder.ToTable("tags");
            builder.HasKey(t => t.Id);

            builder.Property(t => t.Name).HasMaxLength(100).IsRequired();
            builder.Property(t => t.Color).HasMaxLength(7).IsRequired(); // #RRGGBB

            builder.HasIndex(t => t.TenantId);
            builder.HasIndex(t => new { t.TenantId, t.Name }).IsUnique();

            builder.HasOne(t => t.Tenant)
                .WithMany()
                .HasForeignKey(t => t.TenantId)
                .OnDelete(DeleteBehavior.Cascade);

            // Required for tenant isolation. Do not skip this.
            builder.HasQueryFilter(t => t.TenantId == _tenantContext.CurrentTenantId);
        }
    }
    ```
  </Step>

  <Step title="Register the configuration in ApplicationDbContext">
    ```csharp theme={null}
    // src/Acme.Infrastructure/Persistence/ApplicationDbContext.cs

    // Add DbSet:
    public DbSet<Tag> Tags => Set<Tag>();

    // In OnModelCreating, add:
    modelBuilder.ApplyConfiguration(new TagConfiguration(_tenantContext));
    ```
  </Step>

  <Step title="Add to IApplicationDbContext interface">
    ```csharp theme={null}
    // src/Acme.Application/Common/Interfaces/IApplicationDbContext.cs
    DbSet<Tag> Tags { get; }
    ```
  </Step>

  <Step title="Create and apply the migration">
    ```bash theme={null}
    make shell-api
    dotnet ef migrations add AddTagsTable \
      --project src/Acme.Infrastructure \
      --startup-project src/Acme.Api
    exit
    make migrate
    ```

    Verify the migration was applied:

    ```bash theme={null}
    make shell-db
    \dt
    # tags table should appear
    \q
    ```
  </Step>
</Steps>

## Common mistakes

**Forgetting the global query filter** — if you omit `HasQueryFilter`, queries return records from all tenants. No error occurs; data leaks silently. Always add the filter for tenant-scoped entities.

**Forgetting to register the configuration** — if you create `TagConfiguration` but do not call `ApplyConfiguration` in `OnModelCreating`, the entity uses EF Core's conventions and has no filter, no unique index, and no cascade delete.

**Creating the migration before adding DbSet** — EF Core discovers entities through `DbSet` properties. If `DbSet<Tag>` is missing, the migration will not include the table. Add the DbSet first.

**Using `int` for primary keys** — AspFox uses `Guid` PKs throughout. Using `int` breaks the `BaseEntity` contract and makes the admin panel and audit log behave unexpectedly.
