DKNet

Getting Started with DKNet

DKNet is not a framework you adopt wholesale — it is 28 independent NuGet packages. Getting started means picking the two or three you need and wiring them up. This page covers the prerequisites, the smallest setup that actually runs, and where to go next.

Prerequisites

Quick start

1. Install what you need

The Which package do I need? table maps problems to packages. A typical DDD-style API starts with four:

# Entity base classes and the domain-event contracts
dotnet add package DKNet.EfCore.Abstractions

# Entity configuration discovery, global query filters, seeding, GUID v7 keys
dotnet add package DKNet.EfCore.Extensions

# Querying and persistence through IRepositorySpec
dotnet add package DKNet.EfCore.Specifications

# CQRS handlers with automatic SaveChanges
dotnet add package DKNet.SlimBus.Extensions

Add more as the need appears — nothing above depends on the others being present, and no package needs a companion “core” package.

DKNet.EfCore.Repos and DKNet.EfCore.Repos.Abstractions were removed and were never published to NuGet. dotnet add package will not find them. Use DKNet.EfCore.Specifications — see Migrating-Repos-To-Specifications if you are upgrading off them.

2. Define an entity

Derive from AuditedEntity (Guid-keyed, with created/updated tracking and a domain-event queue) or from plain Entity if you do not want the audit fields. Change state through methods, not public setters:

using DKNet.EfCore.Abstractions.Entities;

public class Product : AuditedEntity
{
    private Product() { } // EF Core

    public static Product Create(string name, decimal price, string createdBy)
    {
        var product = new Product { Name = name, Price = price, IsActive = true };
        product.SetCreatedBy(createdBy);
        return product;
    }

    public string Name { get; private set; } = string.Empty;
    public decimal Price { get; private set; }
    public bool IsActive { get; private set; }

    public void Deactivate(string updatedBy)
    {
        IsActive = false;
        SetUpdatedBy(updatedBy);
    }
}

3. Wire it up

AddDbContextWithHook<TDbContext> registers the DbContext and the shared hook interceptor in one call; UseAutoConfigModel<TContext>() is a DbContextOptionsBuilder<TContext> extension, so it goes here rather than in OnModelCreating:

using DKNet.EfCore.Hooks;          // AddDbContextWithHook
using DKNet.EfCore.Specifications; // AddSpecRepo
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContextWithHook<AppDbContext>(options => options
    .UseSqlServer(builder.Configuration.GetConnectionString("Default")!)
    // Discovers every IEntityTypeConfiguration<T> in AppDbContext's assembly
    .UseAutoConfigModel<AppDbContext>());

// One IRepositorySpec serves every entity type in the context
builder.Services.AddSpecRepo<AppDbContext>();

var app = builder.Build();
app.Run();

AddDbContextWithHook is only needed when something hooks into SaveChanges — domain events, audit logs, or ownership stamping. If none of those are in play, a plain AddDbContext<AppDbContext> works and UseAutoConfigModel<AppDbContext>() still applies.

4. Query through a specification

A Specification<TEntity> is configured from its constructor and carries the filter, includes, and ordering as one reusable object:

using DKNet.EfCore.Specifications.Definitions;
using DKNet.EfCore.Specifications.Extensions;
using DKNet.EfCore.Specifications.Repositories;

public sealed class ActiveProductsSpec : Specification<Product>
{
    public ActiveProductsSpec()
    {
        WithFilter(p => p.IsActive);
        AddOrderBy(p => p.Name);
    }
}

public sealed class Catalogue(IRepositorySpec repo)
{
    public Task<IList<Product>> ActiveAsync(CancellationToken cancellationToken = default) =>
        repo.ToListAsync(new ActiveProductsSpec(), cancellationToken);
}

How the packages fit together

Every ring of the onion is a separate package and every dependency points inward. The Architecture Guide has the full picture, including the package dependency graph and two end-to-end walkthroughs (an HTTP request and a domain event):

The DKNet onion: presentation packages on top, the application ring below, the EF Core infrastructure ring in the middle, and DKNet.EfCore.Abstractions plus the dependency-free foundation packages at the centre.

Next steps

  1. Which package do I need? — pick the rest by problem.
  2. Architecture Guide — what a package may depend on, and how a request and an event travel through the suite.
  3. Configuration & Setup — the four registration conventions the packages share, and which page owns each package’s own option table.
  4. Examples & Recipes — a CRUD slice, domain events, specifications, multi-tenancy, blob storage.
  5. SlimBus.ApiEndpoints template — a complete reference implementation in the DKNet.Templates repository.

Common starting points

Goal Read next
A CRUD API with commands and queries separated DKNet.SlimBus.Extensions, then Examples
Side effects that run after a write commits DKNet.EfCore.Events, then A domain event end to end
Row-level isolation between tenants or owners DKNet.EfCore.DataAuthorization, then Examples
A POST a client can safely retry DKNet.AspCore.Idempotency on its own for local development, plus one store package for deployed traffic
Files in Azure, S3, or on disk behind one interface DKNet.Svc.BlobStorage.Abstractions

Getting help