Pular para o conteúdo principal
Versão: 10.4.1

ITLab.Template.DevPack.Infrastructure.EfCore

Holds EF Core-oriented infrastructure shared across products using the DevPack.

Nota de navegação: os interceptors propriamente ditos vivem em Interceptors/README.md. Este README documenta o fluent builder de registro adicionado em 10.2.0.

Componentes

TipoNomeResponsabilidade
NamespaceInterceptorsSave pipeline hooks for auditing, soft-delete and domain event dispatch
Sealed ClassDevPackEfCoreInterceptorBuilderFluent builder for registering interceptors as singletons
Static ClassDevPackEfCoreServiceCollectionExtensionsEntry point AddDevPackEfCoreInterceptors()

Como usar

Forma recomendada (10.2.0+) — fluent builder

using ITLab.Template.DevPack.Infrastructure.EfCore;
using Microsoft.EntityFrameworkCore.Diagnostics;

services.AddDevPackEfCoreInterceptors()
.WithAuditableEntityInterceptor()
.WithSoftDeleteInterceptor()
.WithDispatchDomainEventsInterceptor()
.WithInterceptor<MyOutboxInterceptor>(); // optional, consumer-defined

services.AddDbContext<AppDbContext>((provider, options) =>
{
options.UseSqlServer(connectionString);
options.AddInterceptors(provider.GetServices<ISaveChangesInterceptor>());
});

A ordem dos With* determina a ordem de execução. Mantenha WithAuditableEntityInterceptor() antes de WithDispatchDomainEventsInterceptor() para que o dispatch dos domain events veja a auditoria já populada.

AddDbContext usa provider.GetServices<ISaveChangesInterceptor>() para encaminhar todos os interceptors registrados — DevPack + customizados — sem precisar referenciar tipos individuais.

Forma legada (continua suportada)

services.AddSingleton<AuditableEntityInterceptor>();
services.AddSingleton<SoftDeleteInterceptor>();
services.AddSingleton<DispatchDomainEventsInterceptor>();

services.AddDbContext<AppDbContext>((provider, options) =>
{
options.UseSqlServer(connectionString)
.AddInterceptors(
provider.GetRequiredService<AuditableEntityInterceptor>(),
provider.GetRequiredService<SoftDeleteInterceptor>(),
provider.GetRequiredService<DispatchDomainEventsInterceptor>());
});

Não foi removida em 10.2.0 — só não é mais necessária.

Registro duplo: por tipo concreto E por interface

Cada With* registra o interceptor sob dois identificadores:

services.AddSingleton<AuditableEntityInterceptor>();
services.AddSingleton<ISaveChangesInterceptor>(
sp => sp.GetRequiredService<AuditableEntityInterceptor>());
  • Por tipo concreto — testes que precisam exercitar membros específicos resolvem com provider.GetRequiredService<AuditableEntityInterceptor>().
  • Por ISaveChangesInterceptorAddDbContext itera com provider.GetServices<ISaveChangesInterceptor>() para registrar todos.

A mesma instância é exposta nos dois canais (singleton).

Interceptors customizados

public sealed class OutboxInterceptor(IServiceProvider serviceProvider) : SaveChangesInterceptor
{
public override async ValueTask<InterceptionResult<int>> SavingChangesAsync(...)
{
using var scope = serviceProvider.CreateScope();
var outbox = scope.ServiceProvider.GetRequiredService<IOutboxQueue>();
// ...
}
}

services.AddDevPackEfCoreInterceptors()
.WithAuditableEntityInterceptor()
.WithInterceptor<OutboxInterceptor>();

Como o builder registra o interceptor como singleton, qualquer dependência scoped (banco, current user, etc.) deve ser resolvida via IServiceProvider + CreateScope — mesmo padrão usado por AuditableEntityInterceptor e SoftDeleteInterceptor. Capturar scoped diretamente vaza o escopo.

O que não fazer

  • Do not register interceptors that conflict in entity state transitions without clear ordering.
  • Do not capture scoped services directly in a custom interceptor — use IServiceProvider + CreateScope.
  • Do not call With* for the same interceptor twice unless you want two registrations of the same singleton instance.

EF migrations CLI

O comando dotnet ef migrations add ... precisa de dois parâmetros explícitos quando o DbContext e o entry-point estão em projetos separados (caso típico do template):

  • --project aponta para o projeto onde os arquivos de migration serão gerados (tipicamente Infrastructure, junto ao DbContext).
  • --startup-project aponta para o projeto que tem a composition root do DI (tipicamente Api ou Worker).

Exemplo:

dotnet ef migrations add InitialCreate \
--project src/MyApp.Infrastructure \
--startup-project src/MyApp.Api

Para a receita completa alinhada com o layout do ITLab.Template.Next, ver CLAUDE.md § EF Core commands do template.

Extensão pelo produto

  • Products can add additional interceptors (outbox, telemetry, custom audit) using WithInterceptor<T>().
  • Products can also keep the legacy registration pattern; the builder is opt-in.