Kanject Core

Infrastructure,
Simplified.

Twelve focused .NET libraries — distributed via private NuGet — that wrap the AWS surface in clean, expressive C# APIs. Data, messaging, compute, files, secrets, scheduling — every layer of a production .NET service, source-generated.

Runs in your own AWS account. No vendor data plane, no infra markup — you pay AWS directly; Kanject is a framework license, not a hosting bill.

Latest: NoSqlDatabase now targets ScyllaDB and Keyspaces alongside DynamoDB · new SqlDatabase sibling joins it in beta on Aurora DSQL.

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
Kanject.Core.SqlDatabase
NoSqlDatabase → DynamoDB ScyllaDB Keyspaces
SqlDatabase → Aurora DSQL beta

See your data layer, visually.

Paste your annotated POCOs and DynoStudio derives the tables, the indexes, and the typed query functions — then lets you browse and query without leaving the studio. The same surface drives every Kanject demo call.

Kanject DynoStudio us-east-1
FileEditViewSchemaQueryToolsHelp
ItemsSchemaModelQueryIndexes
No connection yet
Pick an AWS profile, or paste an annotated C# schema to start exploring without a connection.
Connect to AWSPaste SchemaUse LocalStack
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

One [Repository] shape, three engines. Pick AddDynamoNoSqlDatabase(), AddScyllaNoSqlDatabase() or AddKeyspacesNoSqlDatabase() — connection pooling, retries, and type marshalling are handled by the provider so your code never knows the difference.

csharp
// One repository, three engines. Same [Repository] interfaces compile against
// DynamoDB, ScyllaDB, or Amazon Keyspaces — pick the provider extension you
// want at registration time and the rest of your code never knows the
// difference.

using Kanject.Core.NoSqlDatabase.Provider.DynamoDbV2.Extensions;
// or: Kanject.Core.NoSqlDatabase.Provider.Scylla.Extensions;
// or: Kanject.Core.NoSqlDatabase.Provider.Keyspaces.Extensions;

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. The query below
// runs unchanged on DynamoDB, ScyllaDB, or Keyspaces.
public class ProductRepository(INoSqlDatabase db)
{
    public Task<IReadOnlyList<Product>> GetByCategoryAsync(string category)
        => db.QueryAsync<Product>(
            partitionKey: $"category#{category}",
            sortKeyPrefix: "product#",
            limit: 20);
}

Kanject.Core.SqlDatabase — Aurora DSQL · beta

The relational sibling to NoSqlDatabase. Same options pattern, same source-generated [Repository] interfaces — running on Amazon Aurora DSQL. In beta: APIs are stable, but expect provider-level options to land in the next minor release.

csharp
using Kanject.Core.SqlDatabase.Provider.AuroraDsql.Extensions;

// Beta — same options pattern, same self-documenting repositories.
builder.Services.AddAuroraDsqlSqlDatabase(options =>
{
    options.ClusterEndpoint = appSettings.AuroraDsqlEndpoint;
    options.AwsRegion       = appSettings.AwsRegion;
    options.Namespace       = appSettings.DatabaseNamespace;
});

[Repository(table: "Orders")]
public partial interface IOrderRepository : IRepository<Order>
{
    [Query(by: nameof(Order.CustomerId))]
    Task<IReadOnlyList<Order>> ListByCustomerAsync(Guid customerId);
}

public record Order
{
    [PrimaryKey]                   public Guid   Id          { get; init; }
    [Indexed("ByCustomer")]        public Guid   CustomerId  { get; init; }
    public decimal Total      { get; init; }
    public DateTimeOffset PlacedAt { get; init; }
}

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.