DataExportKit 1.0.0

dotnet add package DataExportKit --version 1.0.0
                    
NuGet\Install-Package DataExportKit -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DataExportKit" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DataExportKit" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="DataExportKit" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DataExportKit --version 1.0.0
                    
#r "nuget: DataExportKit, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DataExportKit@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DataExportKit&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=DataExportKit&version=1.0.0
                    
Install as a Cake Tool

DataExportKit

GDPR-compliant personal data export, entity data download (CSV/JSON), and user anonymization for .NET — inspired by spatie/laravel-personal-data-export.

NuGet NuGet CI License: MIT

Table of Contents


Overview

DataExportKit provides a clean, infrastructure-agnostic foundation for GDPR personal data export and right-to-be-forgotten workflows in .NET applications.

  • Infrastructure-agnostic — no EF Core or Dapper dependency. Implement IDataCollector against any data source.
  • Storage-agnostic — no file system or blob storage dependency. Consumers decide where to store or stream the result.
  • Pluggable — register any number of collectors and anonymization handlers per application.
  • Built-in exporters — CSV and JSON exporters with no external libraries.
  • ZIP bundling — multiple exported files are bundled into a single in-memory ZIP archive.

Features

  • GDPR personal data export — collect all data for a user across multiple entity types and bundle it into a ZIP archive
  • Entity data download — export any in-memory collection to CSV or JSON
  • User anonymization — run all registered handlers to fulfill a right-to-be-forgotten request
  • CSV export with configurable delimiter, header control, and property exclusion
  • JSON export with configurable System.Text.Json serializer options
  • In-memory ZIP bundling with optional root folder nesting
  • ASP.NET Core minimal API endpoint helper
  • SourceLink support for debuggable NuGet packages

Packages

Package Description Target Frameworks
DataExportKit Core library — interfaces, exporters, bundler, DI registration net8.0, net9.0, net10.0
DataExportKit.AspNetCore ASP.NET Core integration — minimal API endpoint helpers net8.0, net10.0

Installation

dotnet add package DataExportKit

For ASP.NET Core minimal API endpoint helpers:

dotnet add package DataExportKit.AspNetCore

Quick Start

1. Register services

builder.Services.AddDataExportKit(options =>
{
    options.Register<UserDataCollector>();
    options.Register<OrderDataCollector>();
    options.RegisterAnonymizer<UserAnonymizer>();
    options.RegisterAnonymizer<OrderAnonymizer>();
});

2. Implement a collector

public class UserDataCollector(IUserRepository userRepo) : IDataCollector
{
    public string FileName => "user-profile.json";

    public async Task<ExportData> CollectAsync(string userId, CancellationToken ct)
    {
        var user = await userRepo.GetByIdAsync(userId, ct);
        return ExportData.Json(FileName, new
        {
            user.Email,
            user.DisplayName,
            user.CreatedAt
        });
    }
}

3. Trigger a personal data export

public class ExportController(IDataExporter exporter) : ControllerBase
{
    [HttpGet("export")]
    public async Task<IActionResult> ExportMyData(CancellationToken ct)
    {
        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!;
        var result = await exporter.CreatePersonalDataExportAsync(userId, ct);
        return File(result.ZipContent, "application/zip", result.FileName);
    }
}

Core Concepts

IDataCollector

Implement one IDataCollector per entity type to describe how that entity's data should be collected and formatted.

public interface IDataCollector
{
    string FileName { get; }
    Task<ExportData> CollectAsync(string userId, CancellationToken ct = default);
}

FileName becomes the name of the file inside the ZIP archive. Use descriptive names with the correct extension:

public string FileName => "orders.csv";
public string FileName => "user-profile.json";

IDataExporter

IDataExporter is the main entry point — resolved from DI. It orchestrates all registered collectors and can also export an arbitrary in-memory collection to CSV or JSON.

public interface IDataExporter
{
    Task<ExportResult> CreatePersonalDataExportAsync(string userId, CancellationToken ct = default);

    Task<ExportData> ExportAsync<T>(
        IEnumerable<T> data,
        ExportFormat format,
        string? fileName = null,
        CancellationToken ct = default);
}

IUserAnonymizer and IAnonymizationHandler

IUserAnonymizer orchestrates GDPR right-to-be-forgotten by calling every registered IAnonymizationHandler for the given user.

public interface IUserAnonymizer
{
    Task AnonymizeAsync(string userId, CancellationToken ct = default);
}

public interface IAnonymizationHandler
{
    Task AnonymizeUserDataAsync(string userId, CancellationToken ct = default);
}

Implement one IAnonymizationHandler per entity type:

public class UserAnonymizer(IUserRepository users) : IAnonymizationHandler
{
    public async Task AnonymizeUserDataAsync(string userId, CancellationToken ct)
    {
        var user = await users.GetByIdAsync(userId, ct);
        user.Email = $"deleted-{userId}@anonymized.invalid";
        user.DisplayName = "Deleted User";
        await users.SaveAsync(user, ct);
    }
}

ExportData

Represents a single exported file with its raw bytes and content type. Use the static factory methods for convenience:

// JSON
ExportData.Json("profile.json", myObject);

// CSV
ExportData.Csv("orders.csv", rows);

// Manual
new ExportData
{
    FileName = "custom.bin",
    Content = bytes,
    ContentType = "application/octet-stream"
};

ExportResult

Returned from CreatePersonalDataExportAsync. Contains the ZIP bytes and metadata:

public sealed class ExportResult
{
    public string FileName { get; init; }       // e.g. "personal-data-export-2026-03-24.zip"
    public byte[] ZipContent { get; init; }
    public DateTimeOffset CreatedAt { get; init; }
}

CsvExporter

Static utility for CSV generation. Used internally by ExportData.Csv and IDataExporter.ExportAsync.

var bytes = CsvExporter.Export(rows);
var bytes = CsvExporter.Export(rows, new CsvExportOptions
{
    Delimiter = ";",
    IncludeHeaders = true,
    ExcludeProperties = ["InternalId", "PasswordHash"]
});

JsonExporter

Static utility for JSON generation using System.Text.Json. Used internally by ExportData.Json and IDataExporter.ExportAsync.

// Array
var bytes = JsonExporter.Export(rows);

// Single object
var bytes = JsonExporter.ExportSingle(myObject);

// With options
var bytes = JsonExporter.ExportSingle(myObject, new JsonExportOptions
{
    SerializerOptions = new JsonSerializerOptions { WriteIndented = false }
});

ZipBundler

Static utility for in-memory ZIP creation. Used internally by CreatePersonalDataExportAsync.

var zipBytes = ZipBundler.Bundle(exportDataFiles);

// With a root folder
var zipBytes = ZipBundler.Bundle(exportDataFiles, rootFolder: "my-export");

Registration

All registration goes through AddDataExportKit:

services.AddDataExportKit(options =>
{
    // Register one IDataCollector per entity type
    options.Register<UserDataCollector>();
    options.Register<OrderDataCollector>();
    options.Register<ActivityLogCollector>();

    // Register one IAnonymizationHandler per entity type
    options.RegisterAnonymizer<UserAnonymizer>();
    options.RegisterAnonymizer<OrderAnonymizer>();
});

Collectors and handlers are resolved from the DI container, so they can declare constructor dependencies on any registered service.


Personal Data Export

Call IDataExporter.CreatePersonalDataExportAsync with the user's identifier. All registered IDataCollector implementations are called in parallel, their results are bundled into a ZIP, and an ExportResult is returned.

var result = await dataExporter.CreatePersonalDataExportAsync(userId);

// Stream to browser
return File(result.ZipContent, "application/zip", result.FileName);

// Or save to blob storage
await blobStorage.UploadAsync(result.FileName, result.ZipContent);

The ZIP file name follows the format personal-data-export-{date}.zip, e.g. personal-data-export-2026-03-24.zip.


Exporting Entity Collections

Use IDataExporter.ExportAsync to export any in-memory collection without building a full collector:

// Export to CSV
var csvExport = await dataExporter.ExportAsync(orders, ExportFormat.Csv, "orders.csv");

// Export to JSON
var jsonExport = await dataExporter.ExportAsync(invoices, ExportFormat.Json);

// Use the result bytes
return File(csvExport.Content, csvExport.ContentType, csvExport.FileName);

User Anonymization

Inject IUserAnonymizer and call AnonymizeAsync to run all registered handlers:

public class GdprController(IUserAnonymizer anonymizer) : ControllerBase
{
    [HttpDelete("me")]
    public async Task<IActionResult> DeleteMyAccount(CancellationToken ct)
    {
        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!;
        await anonymizer.AnonymizeAsync(userId, ct);
        return NoContent();
    }
}

Handlers run sequentially in the order they were registered, so you can control the execution order if handlers have dependencies (e.g. anonymize child records before parent records).


ASP.NET Core Integration

Install DataExportKit.AspNetCore for the minimal API endpoint helper:

app.MapPersonalDataExport("/api/personal-data-export");

The endpoint:

  • Requires authorization (RequireAuthorization() is called internally)
  • Resolves the user ID from the sub claim or ClaimTypes.NameIdentifier
  • Returns the ZIP as a file download response

Advanced Usage

CSV Options

var options = new CsvExportOptions
{
    Delimiter = ";",           // use semicolon instead of comma
    IncludeHeaders = false,    // omit the header row
    ExcludeProperties = ["PasswordHash", "SecurityStamp"]
};

var bytes = CsvExporter.Export(users, options);

Fields containing the delimiter, double quotes, or newlines are automatically quoted and escaped per RFC 4180.

JSON Options

var options = new JsonExportOptions
{
    SerializerOptions = new JsonSerializerOptions
    {
        WriteIndented = false,
        PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
    }
};

var bytes = JsonExporter.Export(items, options);

When no options are provided the defaults apply: camelCase property names, indented output, and null values omitted.

Custom ZIP Root Folder

var zipBytes = ZipBundler.Bundle(files, rootFolder: "alice-export");
// ZIP entries: alice-export/profile.json, alice-export/orders.csv

Target Frameworks

Package net8.0 net9.0 net10.0
DataExportKit Yes Yes Yes
DataExportKit.AspNetCore Yes Yes

License

MIT — see LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on DataExportKit:

Package Downloads
DataExportKit.AspNetCore

ASP.NET Core integration for DataExportKit — endpoint helpers and HttpContext-aware DI extensions.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 129 3/26/2026