Skip to content

MediatR

Tayra integrates with MediatR through a pipeline behavior that automatically encrypts and decrypts [PersonalData] fields on requests and responses flowing through the mediator. Your handlers work with plaintext values and never need to know about encryption.

Prerequisites

Tayra.MediatR requires MediatR 12.x or later. Earlier versions use a different IPipelineBehavior signature and are not supported.

Install

shell
dotnet add package Tayra.MediatR
powershell
Install-Package Tayra.MediatR

Setup

Register Tayra core services, a key store, and the MediatR pipeline behavior in your DI container:

csharp
services.AddTayra(opts => opts.LicenseKey = licenseKey); // defaults to InMemoryKeyStore; use PostgreSQL, Vault, etc. in production

services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssemblyContaining<Program>();
});

services.AddTayraMediatR(); // Registers the pipeline behavior

AddTayraMediatR() registers a single IPipelineBehavior<TRequest, TResponse> that wraps every MediatR request/response pipeline. You can optionally pass an Action<TayraMediatROptions> to configure behavior:

csharp
services.AddTayraMediatR(opts =>
{
    opts.EncryptRequests = true;   // default
    opts.DecryptResponses = true;  // default
});

Prerequisites

Tayra core services must be registered via services.AddTayra() (optionally chaining a production key store) before AddTayraMediatR() is called. The pipeline behavior resolves ITayra from DI at runtime.

Options

csharp
services.AddTayraMediatR(opts =>
{
    opts.EncryptRequests = true;   // default
    opts.DecryptResponses = true;  // default
});
PropertyTypeDefaultDescription
EncryptRequestsbooltrueEncrypt PII fields on the request object before passing it to the handler
DecryptResponsesbooltrueDecrypt PII fields on the response object after the handler returns

Requests and Responses

Annotate your MediatR request and response classes with [DataSubjectId] and [PersonalData]:

csharp
public class CreateCustomerCommand : IRequest<CreateCustomerResult>
{
    [DataSubjectId]
    public string CustomerId { get; set; }

    [PersonalData]
    public string Name { get; set; }

    [PersonalData]
    public string Email { get; set; }

    public string AccountType { get; set; } // Not encrypted
}

public class CreateCustomerResult
{
    [DataSubjectId]
    public string CustomerId { get; set; }

    [PersonalData]
    public string Name { get; set; }

    [PersonalData]
    public string Email { get; set; }

    public DateTime CreatedAt { get; set; }
}

How the Pipeline Behavior Works

MediatR processes requests through a chain of IPipelineBehavior<TRequest, TResponse> instances. Tayra registers a single behavior that wraps the entire pipeline:

  1. Before the handler -- The behavior scans the TRequest for [PersonalData] fields. If EncryptRequests is enabled, it encrypts all annotated fields so that PII is protected before reaching the handler. If your handler needs cleartext (the typical case), set EncryptRequests = false and rely on the upstream caller to send encrypted data, or leave it enabled when you want the handler to work with encrypted values (e.g., for pass-through persistence).

  2. After the handler -- The behavior scans the TResponse for [PersonalData] fields. If DecryptResponses is enabled, it decrypts all annotated fields so your calling code receives plaintext.

A typical in-process workflow where the handler needs plaintext looks like this:

csharp
services.AddTayraMediatR(opts =>
{
    opts.EncryptRequests = false;  // Handler receives cleartext
    opts.DecryptResponses = true;  // Caller receives cleartext
});

Your handlers work with plaintext values and do not need to know about encryption:

csharp
public class CreateCustomerHandler
    : IRequestHandler<CreateCustomerCommand, CreateCustomerResult>
{
    public async Task<CreateCustomerResult> Handle(
        CreateCustomerCommand request,
        CancellationToken cancellationToken)
    {
        // request.Name and request.Email are plaintext
        var customer = await SaveCustomer(request);

        return new CreateCustomerResult
        {
            CustomerId = customer.Id,
            Name = customer.Name,       // Will be decrypted if needed
            Email = customer.Email,     // Will be decrypted if needed
            CreatedAt = customer.CreatedAt,
        };
    }
}

Graceful Error Handling

If encryption or decryption fails (e.g., a key store is temporarily unavailable), the pipeline behavior logs a warning and allows the request to proceed. This prevents transient key store failures from blocking request processing.

Notifications

The pipeline behavior only applies to IRequest<TResponse> pipelines. MediatR notifications (INotification) do not pass through IPipelineBehavior and are not intercepted by Tayra. If you need to protect PII in notification handlers, call ITayra directly:

csharp
public class CustomerCreatedNotificationHandler
    : INotificationHandler<CustomerCreatedNotification>
{
    private readonly ITayra _tayra;

    public CustomerCreatedNotificationHandler(ITayra tayra)
    {
        _encrypter = encrypter;
    }

    public async Task Handle(
        CustomerCreatedNotification notification,
        CancellationToken cancellationToken)
    {
        await _encrypter.DecryptAsync(notification);
        // notification.Name and notification.Email are now plaintext
    }
}

Use with EF Core

MediatR is commonly paired with EF Core in CQRS-style applications. When both Tayra.MediatR and Tayra.EFCore are registered, be careful to avoid double-encryption. A recommended pattern:

csharp
// MediatR behavior handles request/response PII
services.AddTayraMediatR(opts =>
{
    opts.EncryptRequests = false;  // Let EF Core handle persistence encryption
    opts.DecryptResponses = true;
});

// EF Core interceptors handle storage-level PII
services.AddDbContext<AppDbContext>((sp, opts) =>
{
    opts.UseNpgsql(connectionString)
        .UseTayra(sp);
});

This way, MediatR handlers work with plaintext, EF Core encrypts on save, and the response is decrypted for the caller.

See Also