Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Orbis25/FoundationKit/llms.txt

Use this file to discover all available pages before exploring further.

Every entity that inherits from BaseModel carries an IsDeleted flag for soft-delete support. For that flag to be useful, it must be enforced at the query layer automatically — otherwise every developer on the team has to remember to add a .Where(x => !x.IsDeleted) clause to every LINQ query. EFCoreConfiguration<TEntity> solves this by registering a global EF Core query filter the moment a configuration class is applied, and then delegating to your own ConfigureEF implementation for entity-specific column mappings, indexes, and relationships.

EFCoreConfiguration<TEntity>

namespace FoundationKit.Domain.Config;

public abstract class EFCoreConfiguration<TEntity> : IEntityTypeConfiguration<TEntity>
    where TEntity : BaseModel
{
    public void Configure(EntityTypeBuilder<TEntity> builder)
    {
        builder.HasQueryFilter(x => !x.IsDeleted);
        ConfigureEF(builder);
    }

    public abstract void ConfigureEF(EntityTypeBuilder<TEntity> builder);
}

Members

Configure(EntityTypeBuilder<TEntity> builder)
void
The method called by EF Core’s model-building pipeline when it applies this configuration. It first registers HasQueryFilter(x => !x.IsDeleted) on the entity, then forwards to ConfigureEF for any additional configuration you supply. You should not override or call this method yourself.
ConfigureEF(EntityTypeBuilder<TEntity> builder)
abstract void
Implement this method to provide entity-specific configuration — column names, string lengths, required constraints, indexes, relationships, and so on. It is called by Configure after the query filter has been applied, so the filter is always in place regardless of what you do inside ConfigureEF.

The global query filter

Calling HasQueryFilter(x => !x.IsDeleted) in Configure instructs EF Core to append AND IsDeleted = 0 (or the database equivalent) to every SQL query that targets TEntity, including those performed through navigation properties. This means your repositories and service methods stay clean:
// This automatically excludes soft-deleted people — no extra .Where() needed
var people = await dbContext.People.ToListAsync();
When you genuinely need to include soft-deleted records — for example in an admin restore endpoint or a data-export job — call .IgnoreQueryFilters() on the specific query:
var allIncludingDeleted = await dbContext.People
    .IgnoreQueryFilters()
    .Where(p => p.IsDeleted)
    .ToListAsync();
IgnoreQueryFilters() removes all global filters from that query, not just the soft-delete one, so be mindful if you have applied additional filters in your configuration.

Implementing a configuration class

Derive from EFCoreConfiguration<TEntity> and implement ConfigureEF to describe your entity’s schema. The example below configures a Person entity with custom column names, a max-length constraint, a unique index on the email address, and a required relationship to a Department.
using FoundationKit.Domain.Config;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

public class PersonConfiguration : EFCoreConfiguration<Person>
{
    public override void ConfigureEF(EntityTypeBuilder<Person> builder)
    {
        builder.ToTable("people");

        builder.Property(p => p.FirstName)
               .HasColumnName("first_name")
               .HasMaxLength(120)
               .IsRequired();

        builder.Property(p => p.LastName)
               .HasColumnName("last_name")
               .HasMaxLength(120)
               .IsRequired();

        builder.Property(p => p.Email)
               .HasColumnName("email")
               .HasMaxLength(256)
               .IsRequired();

        builder.HasIndex(p => p.Email)
               .IsUnique();

        builder.HasOne(p => p.Department)
               .WithMany(d => d.People)
               .HasForeignKey(p => p.DepartmentId)
               .OnDelete(DeleteBehavior.Restrict);
    }
}

Registering configurations in OnModelCreating

Register all configurations at once by scanning the assembly that contains them:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    // Discovers and applies every IEntityTypeConfiguration in the assembly
    modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
Or register individual configurations manually:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.ApplyConfiguration(new PersonConfiguration());
    modelBuilder.ApplyConfiguration(new OrderConfiguration());
}
ApplyConfigurationsFromAssembly is the recommended approach for large projects. It uses reflection to find every concrete class that implements IEntityTypeConfiguration<T> in the given assembly, so new configurations are picked up automatically without touching OnModelCreating.

MigrationDbContextConfigAssembly

MigrationDbContextConfigAssembly is an advanced utility that extends EF Core’s internal MigrationsAssembly to support schema-aware migrations. It enables migrations to accept an IDbContextSchema constructor parameter, allowing the same migration class to run against multiple schemas at runtime.
// Implement the marker interface on your DbContext to provide the schema name
public class AppDbContext : FoundationKitDbContext, IDbContextSchema
{
    public string Schema => "app";
    // ...
}
Register the replacement assembly class in your DbContext options to activate schema-aware migration creation:
builder.Services.AddDbContext<AppDbContext>(options =>
    options
        .UseSqlServer(connectionString)
        .ReplaceService<IMigrationsAssembly, MigrationDbContextConfigAssembly>());
MigrationDbContextConfigAssembly uses internal EF Core APIs (EF1001). While it works correctly with the EF Core versions targeted by FoundationKit, it may require adjustment when upgrading to a future major version of EF Core.

EncryptorOption

EncryptorOption is a configuration class for FoundationKit’s encryption utilities. Bind it from appsettings.json or register it through the options pattern.
namespace FoundationKit.Domain.Option;

public class EncryptorOption
{
    public string?    PrivateKey { get; set; }
    public string?    PublicKey  { get; set; }
    public Encoding?  Enconding  { get; set; }
    public string?    HeaderAes  { get; set; }
}
PublicKey
string?
Base64-encoded public key used for asymmetric encryption operations. Store this in a secrets manager or an environment variable rather than directly in appsettings.json.
PrivateKey
string?
Base64-encoded private key. Must be kept confidential. Never commit this value to source control.
HeaderAes
string?
The name of the HTTP request header that carries AES configuration data (for example "AES"). Used by FoundationKit middleware to locate the AES context for a given request.
Enconding
System.Text.Encoding?
The text encoding used when converting between strings and byte arrays during encryption and decryption. Note: the property name is spelled Enconding (without the second d) in the source — use that exact name when binding from configuration or referencing the property in code.

Binding from appsettings.json

{
  "EncryptorOption": {
    "PublicKey": "<base64-public-key>",
    "PrivateKey": "<base64-private-key>",
    "HeaderAes": "AES"
  }
}
builder.Services.Configure<EncryptorOption>(
    builder.Configuration.GetSection("EncryptorOption"));

Build docs developers (and LLMs) love