Kanject Core

Infrastructure,
Simplified.

A suite of focused .NET libraries — distributed via private NuGet — that wrap AWS services into clean, expressive C# APIs. Stop fighting SDKs. Ship in minutes, not days.

Kanject.Core.Adapter
Kanject.Core.Api
Kanject.Core.CacheDb
Kanject.Core.CloudFunction
Kanject.Core.EtlTaskManager
Kanject.Core.FileRepository
Kanject.Core.Logs
Kanject.Core.NoSqlDatabase
Kanject.Core.Queue
Kanject.Core.Recurring
Kanject.Core.Secrets
Before & After

Stop writing boilerplate. Start shipping.

The same DynamoDB query — on the left, raw AWS SDK with manual credential wiring, expression attributes, and dictionary marshalling. On the right, idiomatic C# with Kanject.Core.NoSqlDatabase. Drag to compare.

60+
Lines eliminated
1
Line to register
0
Config files
Kanject.Core· clean, typed, testable
csharp
using Kanject.Core.NoSqlDatabase;

public class ProductRepository(INoSqlDatabase db)
{
    public Task<IReadOnlyList<Product>> GetByCategoryAsync(string category)
        => db.QueryAsync<Product>(
            partitionKey: $"category#{category}",
            sortKeyPrefix: "product#",
            limit: 20);

    public Task CreateAsync(Product product)
        => db.PutAsync(product);
}

// That's it. Credentials, region, marshalling, retries —
// all wired via AddDynamoNoSqlDatabase() in Program.cs.
AWS SDK· verbose, error-prone
csharp
using Amazon;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;

var credentials = new BasicAWSCredentials(
    Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID"),
    Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY"));

var client = new AmazonDynamoDBClient(credentials,
    new AmazonDynamoDBConfig { RegionEndpoint = RegionEndpoint.EUWest1 });

// Query products by category
var queryRequest = new QueryRequest
{
    TableName = "products",
    KeyConditionExpression = "pk = :pk AND begins_with(sk, :sk)",
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>
    {
        { ":pk", new AttributeValue { S = "category#electronics" } },
        { ":sk", new AttributeValue { S = "product#" } },
    },
    Limit = 20,
};

var response = await client.QueryAsync(queryRequest);
var products = new List<Product>();
foreach (var item in response.Items)
{
    products.Add(new Product
    {
        Id    = item["id"].S,
        Name  = item["name"].S,
        Price = decimal.Parse(item["price"].N),
        Stock = int.Parse(item["stock"].N),
    });
}

// Write a new product (manual marshalling)
await client.PutItemAsync(new PutItemRequest
{
    TableName = "products",
    Item = new Dictionary<string, AttributeValue>
    {
        { "pk",    new AttributeValue { S = "category#electronics" } },
        { "sk",    new AttributeValue { S = "product#001" } },
        { "id",    new AttributeValue { S = "product#001" } },
        { "name",  new AttributeValue { S = "Widget Pro" } },
        { "price", new AttributeValue { N = "99.99" } },
        { "stock", new AttributeValue { N = "42" } },
    },
});
AWS SDKdrag to compareKanject.Core
Source Generators & Analyzers

You write this. We generate that.

Kanject.Core ships with Roslyn source generators and analyzers that turn a handful of attributes into a full, type-safe repository layer — compiled at build time. No reflection, no runtime surprises, no wrong-way-to-hold-it.

csharp
// <auto-generated> by Kanject.SourceGen — do not edit </auto-generated>
// Generated at build time from [Repository], [EntityRepository<T>],
// [EmbedRepository<T>] and [DbContext] attributes.
namespace Kanject.Insights.Provider.Aws.Abstractions.Repositories;

public partial class CohortInsightRepository : RepositoryBase<CohortInsightEntity>
{
    private readonly KanjectInsightDbContext _dbContext;
    private readonly IEntityRepository<CohortInsightDefinitionEntity>       _definitions;
    private readonly IEntityRepository<CohortActivityInsightDefinitionEntity> _activityDefs;
    private readonly IEntityRepository<List<CohortInsightMetricEntity>>     _metrics;
    private readonly IEntityRepository<List<CohortInsightFilterEntity>>     _filters;
    private readonly IEntityRepository<List<CohortInsightAlertRuleEntity>>  _alertRules;
    private readonly IEntityRepository<List<CohortInsightAlertStateEntity>> _alertStates;
    private readonly CohortSnapshotRepository                                _snapshots;

    public CohortInsightRepository(
        KanjectInsightDbContext dbContext,
        IEntityRepository<CohortInsightDefinitionEntity> definitions,
        IEntityRepository<CohortActivityInsightDefinitionEntity> activityDefs,
        IEntityRepository<List<CohortInsightMetricEntity>> metrics,
        IEntityRepository<List<CohortInsightFilterEntity>> filters,
        IEntityRepository<List<CohortInsightAlertRuleEntity>> alertRules,
        IEntityRepository<List<CohortInsightAlertStateEntity>> alertStates,
        CohortSnapshotRepository snapshots)
        : base(dbContext, version: 2)
    {
        _dbContext    = dbContext;
        _definitions  = definitions;
        _activityDefs = activityDefs;
        _metrics      = metrics;
        _filters      = filters;
        _alertRules   = alertRules;
        _alertStates  = alertStates;
        _snapshots    = snapshots;
    }

    // + 340 lines: typed getters, migrations, health checks,
    //   DI registration, batch helpers, analyzer hints…
}
Compile-time safe
Analyzers flag missing DbContext, bad entity types, and wrong versions before you run a single test.
Zero reflection
Every dependency is wired at build time. Cold-start is instant; there’s no runtime scanning.
Harder to misuse
The generated API only exposes correct shapes — you literally can’t call it the wrong way.
Compile-time docs · AI-ready

Every build emits a manual.

Every Kanject Core library writes a comprehensive Markdown spec at compile time — generated repositories, key templates, indexes, queues, event topics, cache contexts. The same source-generator pass that emits typed C# also emits the README, in full. Drop the file straight into your AI assistant for instant, accurate codebase context.

csharp
using Kanject.Core.NoSqlDatabase.Provider.DynamoDb.Annotations.Attributes;

[Repository(Entity = typeof(CountryEntity), Version = 2)]
[DbContext(typeof(ConfigurationDbContext))]
[EntityRepository<CountryDialingCodeEntity>]
[EntityRepository<List<CountrySeasonEntity>>]
[EntityRepository<List<CountrySetupEntity>>]
[EmbedRepository<PayoutProviderRepository>]
[EmbedRepository<ShippingProviderRepository>]
[DynamoDbGsi(name: "country-by-iso", pk: "iso2", sk: "name")]
public partial class CountryRepository;

// 11 lines. The source generator does the rest…
Trifted.Configuration.Data.dbschema.md 543 KB

Trifted.Configuration.Data — DynamoDB Schema Reference

Auto-generated by Kanject DynamoDB Source Generator. Do not edit manually.
Database Contexts1
Repositories19
Global Secondary Indexes2
Item Collections1
Embedded Repositories7
Unique Entities29
Key Templates84
Kanject Indexes26
  1. Quick Overview
  2. Core Concepts & Glossary
  3. Common Tasks
  4. Constraints & Invariants
  5. Single-Table Key Design
  6. Database Contexts
  7. Repositories 19
  8. Global Secondary Indexes
  9. Capacity & Throughput
  10. Hot Partition Risk Analysis
  11. WCU/RCU Cost per Access Pattern
  12. CloudWatch Alarm Recommendations
  13. Security Posture & IAM Actions
  14. Method Quick Reference
  15. … 13 more sections
543 KB
Per assembly
Real example: Trifted.Configuration.Data
27 sections
Catalogued
Repos · indexes · keys · capacity · IAM
~490
Generated methods
Cataloged with signatures and call sites
AI-ready
Drop into context
Designed for Claude, Cursor, Copilot, ChatGPT

Kanject.Core.NoSqlDatabase

A clean .NET wrapper around AWS DynamoDB. Handles connection pooling, error retries, and type marshalling automatically — so you write clean, readable C# instead of fighting the SDK.

csharp
using Kanject.Core.NoSqlDatabase.Provider.DynamoDbV2.Extensions;

// Register once in Program.cs — options pattern, env-aware
builder.Services.AddDynamoNoSqlDatabase(options =>
{
    options.Namespace = appSettings.DatabaseNamespace;
    options.AccessKey = appSettings.AwsAccessKeyId;
    options.SecretKey = appSettings.AwsAccessSecretKey;
    options.AwsRegion = appSettings.AwsRegion;

#if DEBUG
    options.EnableDbMigration();
    options.EnableDebugLogging();
#endif
});

// Inject anywhere — self-documenting repository pattern
public class ProductRepository(INoSqlDatabase db)
{
    public Task<IReadOnlyList<Product>> GetByCategoryAsync(string category)
        => db.QueryAsync<Product>(
            partitionKey: $"category#{category}",
            sortKeyPrefix: "product#",
            limit: 20);
}

Kanject.Core.Queue + Logs

Async-first SQS wrapper with built-in dead-letter queue support and retry logic. Pair with Kanject.Core.Logs for structured, correlated log output — all in a few lines of C#.

csharp
using Kanject.Core.Queue.Extensions;
using Kanject.Core.Logs.Extensions;

builder.Services.AddSqsQueue(options =>
{
    options.AwsRegion = appSettings.AwsRegion;
    options.DeadLetterQueueEnabled = true;
    options.MaxRetries = 3;
});

builder.Services.AddKanjectLogs();

// Inject — clean async API, structured logs correlated by request
public class OrderService(IQueueService queue, ILogger<OrderService> log)
{
    public async Task PlaceAsync(Order order)
    {
        await queue.SendAsync("order-processing", order);
        log.LogInformation("Order queued {OrderId}", order.Id);
    }
}

Kanject.Core.FileRepository + Secrets

File storage done right — automatic content-type detection, presigned URLs, and access control. Pair with Kanject.Core.Secrets for safe AWS Secrets Manager retrieval in any environment.

csharp
using Kanject.Core.FileRepository.Provider.AwsS3.Extensions;
using Kanject.Core.Secrets.Extensions;

builder.Services.AddAwsS3FileRepository(options =>
{
    options.BucketName        = fileSettings.BucketName;
    options.Region            = appSettings.AwsRegion;
    options.FileServerBaseUrl = fileSettings.FileServerBaseUrl;
    options.ResourceTypeDefinitions.AddResourceTypeDefinitions();
});

builder.AddAwsSystemManagerParameterStore(); // zero-config secrets

// Inject both — presigned URLs, content-type auto-detection
public class AvatarService(IFileRepository files, ISecretsProvider secrets)
{
    public Task<string> UploadAsync(Stream stream, Guid userId)
        => files.UploadAsync(stream, $"avatars/{userId}.png", isPublic: true);
}
Ready to ship?

Simplify your cloud
journey today.

Join forward-thinking developers and businesses who trust Kanject to eliminate cloud complexity and accelerate innovation.