Skip to content

GrydCrud + GrydAudit Integration ​

This guide explains how to enable automatic audit logging for entities managed by GrydCrud.

Overview ​

GrydCrud and GrydAudit integrate seamlessly through:

  1. Entity Interface: Mark entities with IAuditableEntity
  2. EF Core Interceptor: AuditSaveChangesInterceptor automatically captures changes
  3. Tracing Decorators: Optional distributed tracing for CRUD operations

Quick Setup ​

1. Mark Entity as Auditable ​

csharp
using Gryd.Domain.Abstractions;
using Gryd.Observability.Audit;

public class Product : IEntity, IAuditableEntity
{
    public Guid Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public decimal Price { get; set; }
    public string? Description { get; set; }
    
    // Exclude sensitive properties from audit
    public IEnumerable<string> AuditExcludedProperties => ["InternalCode"];
    
    // Categorize for audit queries
    public string? AuditCategory => "Inventory";
}

2. Configure Services ​

csharp
// Program.cs
var builder = WebApplication.CreateBuilder(args);

// 1. Add GrydCrud with operations
builder.Services.AddGrydCrudCore();
builder.Services.AddCrudOperations<Product, ProductCreateDto, ProductUpdateDto, ProductDto>();

// 2. Add distributed tracing (optional)
builder.Services.AddGrydObservability(config => config.ServiceName = "MyApi");
builder.Services.AddCrudTracing<Product, ProductCreateDto, ProductUpdateDto, ProductDto>();

// 3. Add GrydAudit with interceptor
builder.Services.AddGrydAudit(options => options.UseSqlServer(connectionString));
builder.Services.AddAuditInterceptor();

3. Register Interceptor in DbContext ​

csharp
// In your DbContext configuration
services.AddDbContext<AppDbContext>((sp, options) =>
{
    options.UseSqlServer(connectionString);
    
    // Add the audit interceptor
    var auditInterceptor = sp.GetService<AuditSaveChangesInterceptor>();
    if (auditInterceptor is not null)
    {
        options.AddInterceptors(auditInterceptor);
    }
});

How It Works ​

Automatic Change Tracking ​

When GrydCrud operations call SaveChangesAsync(), the AuditSaveChangesInterceptor:

  1. Detects entities implementing IAuditableEntity
  2. Captures the change type (Created, Updated, Deleted)
  3. Records property changes with old/new values
  4. Stores audit logs with 5W1H context

Audit Log Contents ​

Each audit log includes:

FieldDescriptionSource
UserIdWho made the changeIAuditContext
EntityTypeEntity class nameReflection
EntityIdEntity primary keyEF Core
ActionCreated/Updated/DeletedEF Core ChangeTracker
TimestampWhen it happenedUTC now
TenantIdMulti-tenant contextIAuditContext
PropertyChangesOld → New valuesEF Core ChangeTracker

Distributed Tracing ​

With tracing enabled via AddCrudTracing<>():

[Crud.Create.Product] ──┬── entity.type: Product
                        ├── action: Create
                        ├── operation.success: true
                        └── duration: 45ms

Advanced Configuration ​

Conditional Audit ​

Disable audit for specific entities conditionally:

csharp
public class DraftDocument : IEntity, IAuditableEntity
{
    public Guid Id { get; set; }
    public string Content { get; set; }
    public bool IsDraft { get; set; }
    
    // Only audit when published
    public bool AuditEnabled => !IsDraft;
}

Exclude Sensitive Properties ​

csharp
public class User : IEntity, IAuditableEntity
{
    public Guid Id { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    public string SecurityStamp { get; set; }
    
    // Never audit password-related fields
    public IEnumerable<string> AuditExcludedProperties => 
        ["PasswordHash", "SecurityStamp", "RefreshToken"];
}

Custom Audit Categories ​

csharp
public class PaymentTransaction : IEntity, IAuditableEntity
{
    public Guid Id { get; set; }
    public decimal Amount { get; set; }
    
    // Financial category for compliance reports
    public string? AuditCategory => "Financial";
}

Querying Audit Logs ​

By Entity ​

csharp
// Get all changes to a specific product
var history = await mediator.Send(new GetAuditLogsByEntityQuery("Product", productId));

By User ​

csharp
// Get all changes made by a user
var userActivity = await mediator.Send(new GetAuditLogsByUserQuery(userId));

With Filters ​

csharp
// Complex search
var results = await mediator.Send(new SearchAuditLogsQuery(new AuditLogFilter
{
    EntityType = "Product",
    Action = EntityChangeType.Updated,
    FromDate = DateTime.UtcNow.AddDays(-7),
    TenantId = tenantId
}));

Best Practices ​

  1. Always exclude sensitive data — Passwords, tokens, PII. See LGPD & Data Protection
  2. Use categories — Makes reporting and compliance easier
  3. Enable tracing in production — Valuable for debugging
  4. Set retention policies — Don't store audit logs forever
  5. Index audit tables — EntityType, EntityId, Timestamp for performance

Troubleshooting ​

Audit logs not being created ​

  1. Verify entity implements IAuditableEntity
  2. Check AuditEnabled returns true
  3. Ensure interceptor is registered in DbContext
  4. Verify IAuditContext is populated

Missing property changes ​

  1. Check property is not in AuditExcludedProperties
  2. Verify property is a public get/set property
  3. Complex types may need special handling

Performance issues ​

  1. Use async processing for high-volume entities
  2. Consider separate database for audit logs
  3. Implement audit log archiving

Released under the MIT License.