.NET / C# Integration
This guide covers connecting to every WeftKit engine from .NET and C#. All examples use standard NuGet packages and widely-used frameworks — no WeftKit-specific SDK is required. Point your existing driver at weftkit-standalone and your existing application code works as-is.
Prerequisites
Add the following NuGet packages to your project. Pick the sections relevant to the engines you are using.
NuGet Package References (.csproj)
xml<!-- WeftKitRel — Npgsql (PostgreSQL) --> <PackageReference Include="Npgsql" Version="8.0.3" /> <!-- WeftKitRel — Entity Framework Core + Npgsql provider --> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4" /> <!-- WeftKitRel — Dapper --> <PackageReference Include="Dapper" Version="2.1.35" /> <!-- WeftKitDoc — MongoDB .NET Driver --> <PackageReference Include="MongoDB.Driver" Version="2.26.0" /> <!-- WeftKitMem — StackExchange.Redis --> <PackageReference Include="StackExchange.Redis" Version="2.7.33" /> <!-- WeftKitGraph — Neo4j .NET Driver --> <PackageReference Include="Neo4j.Driver" Version="5.21.0" /> <!-- WeftKitKV — AWS SDK for .NET (DynamoDB) --> <PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.303" />
Or install via the .NET CLI:
bash# PostgreSQL drivers dotnet add package Npgsql dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL dotnet add package Microsoft.EntityFrameworkCore.Design dotnet add package Dapper # MongoDB dotnet add package MongoDB.Driver # Redis dotnet add package StackExchange.Redis # Neo4j dotnet add package Neo4j.Driver # DynamoDB dotnet add package AWSSDK.DynamoDBv2
WeftKitRel — PostgreSQL via Npgsql
WeftKitRel speaks the PostgreSQL v3 wire protocol, so the standard Npgsql driver connects without any modification.
Default port: 5432
Basic Connection and Query
csharpusing Npgsql; // Connection string format — identical to standard PostgreSQL const string connStr = "Host=localhost;Port=5432;Database=mydb;Username=app_user;Password=secret"; // --- INSERT with parameterised command --- await using var conn = new NpgsqlConnection(connStr); await conn.OpenAsync(); await using var insertCmd = new NpgsqlCommand( "INSERT INTO products (name, price, stock) VALUES (@name, @price, @stock) RETURNING id", conn); insertCmd.Parameters.AddWithValue("name", "Widget Pro"); insertCmd.Parameters.AddWithValue("price", 29.99); insertCmd.Parameters.AddWithValue("stock", 250); long newId = (long)(await insertCmd.ExecuteScalarAsync())!; Console.WriteLine($"Inserted product id: {newId}"); // --- SELECT and iterate results --- await using var selectCmd = new NpgsqlCommand( "SELECT id, name, price FROM products WHERE price < @max ORDER BY price", conn); selectCmd.Parameters.AddWithValue("max", 50.0); await using var reader = await selectCmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { long id = reader.GetInt64(0); string name = reader.GetString(1); double price = reader.GetDouble(2); Console.WriteLine($" [{id}] {name} — ${price:F2}"); }
Transaction Management
csharpusing Npgsql; const string connStr = "Host=localhost;Port=5432;Database=mydb;Username=app_user;Password=secret"; static async Task TransferFundsAsync(long fromId, long toId, decimal amount) { await using var conn = new NpgsqlConnection(connStr); await conn.OpenAsync(); await using var tx = await conn.BeginTransactionAsync(); try { // Debit source account await using var debitCmd = new NpgsqlCommand( "UPDATE accounts SET balance = balance - @amount WHERE id = @id", conn, tx); debitCmd.Parameters.AddWithValue("amount", amount); debitCmd.Parameters.AddWithValue("id", fromId); await debitCmd.ExecuteNonQueryAsync(); // Credit destination account await using var creditCmd = new NpgsqlCommand( "UPDATE accounts SET balance = balance + @amount WHERE id = @id", conn, tx); creditCmd.Parameters.AddWithValue("amount", amount); creditCmd.Parameters.AddWithValue("id", toId); await creditCmd.ExecuteNonQueryAsync(); await tx.CommitAsync(); Console.WriteLine("Transfer committed."); } catch { await tx.RollbackAsync(); Console.WriteLine("Transfer rolled back."); throw; } }
Bulk Copy with NpgsqlBinaryImporter
csharpusing Npgsql; static async Task BulkImportProductsAsync(IEnumerable<(string Name, double Price, int Stock)> rows) { await using var conn = new NpgsqlConnection(connStr); await conn.OpenAsync(); await using var writer = await conn.BeginBinaryImportAsync( "COPY products (name, price, stock) FROM STDIN (FORMAT BINARY)"); foreach (var (name, price, stock) in rows) { await writer.StartRowAsync(); await writer.WriteAsync(name, NpgsqlTypes.NpgsqlDbType.Text); await writer.WriteAsync(price, NpgsqlTypes.NpgsqlDbType.Double); await writer.WriteAsync(stock, NpgsqlTypes.NpgsqlDbType.Integer); } ulong rowsWritten = await writer.CompleteAsync(); Console.WriteLine($"Bulk imported {rowsWritten} rows."); }
WeftKitRel — Entity Framework Core + Npgsql
appsettings.json
json{ "ConnectionStrings": { "WeftKitRel": "Host=localhost;Port=5432;Database=mydb;Username=app_user;Password=secret" } }
Model Class
csharpusing System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; [Table("orders")] public class Order { [Key] [Column("id")] public long Id { get; set; } [Required] [Column("customer_id")] public long CustomerId { get; set; } [Required] [Column("total_amount")] public decimal TotalAmount { get; set; } [Column("status")] [MaxLength(32)] public string Status { get; set; } = "pending"; [Column("created_at")] public DateTime CreatedAt { get; set; } = DateTime.UtcNow; }
DbContext
csharpusing Microsoft.EntityFrameworkCore; public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<Order> Orders { get; set; } = null!; public DbSet<Product> Products { get; set; } = null!; protected override void OnModelCreating(ModelBuilder builder) { builder.Entity<Order>() .HasIndex(o => o.CustomerId); builder.Entity<Order>() .Property(o => o.CreatedAt) .HasDefaultValueSql("NOW()"); } }
ASP.NET Core DI Registration
csharp// Program.cs var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext<AppDbContext>(options => options.UseNpgsql(builder.Configuration.GetConnectionString("WeftKitRel"))); // Or use NpgsqlDataSource directly (recommended for .NET 7+) builder.Services.AddNpgsqlDataSource( builder.Configuration.GetConnectionString("WeftKitRel")!); var app = builder.Build();
LINQ Queries and CRUD
csharpusing Microsoft.EntityFrameworkCore; public class OrderService { private readonly AppDbContext _db; public OrderService(AppDbContext db) => _db = db; // --- Read: async list query --- public async Task<List<Order>> GetPendingOrdersAsync() => await _db.Orders .Where(o => o.Status == "pending") .OrderBy(o => o.CreatedAt) .ToListAsync(); // --- Read: single item --- public async Task<Order?> GetOrderAsync(long id) => await _db.Orders.FirstOrDefaultAsync(o => o.Id == id); // --- Create --- public async Task<Order> PlaceOrderAsync(long customerId, decimal amount) { var order = new Order { CustomerId = customerId, TotalAmount = amount }; await _db.Orders.AddAsync(order); await _db.SaveChangesAsync(); return order; } // --- Update --- public async Task ShipOrderAsync(long orderId) { var order = await _db.Orders.FindAsync(orderId) ?? throw new KeyNotFoundException($"Order {orderId} not found."); order.Status = "shipped"; await _db.SaveChangesAsync(); } // --- Delete --- public async Task CancelOrderAsync(long orderId) { var order = await _db.Orders.FindAsync(orderId); if (order is not null) { _db.Orders.Remove(order); await _db.SaveChangesAsync(); } } }
Migrations
bash# Add a new migration (run from the project directory) dotnet ef migrations add InitialSchema # Apply migrations to the running WeftKit instance dotnet ef database update # Generate SQL script instead of applying directly dotnet ef migrations script --output migrations.sql
WeftKitRel — Dapper
Dapper provides lightweight object mapping on top of any IDbConnection.
csharpusing Dapper; using Npgsql; using System.Data; public class ProductRepository { private readonly string _connStr; public ProductRepository(string connectionString) => _connStr = connectionString; private IDbConnection Connection() => new NpgsqlConnection(_connStr); // --- QueryAsync<T>: map rows to typed POCOs --- public async Task<IEnumerable<Product>> FindByMaxPriceAsync(double maxPrice) { using var conn = Connection(); return await conn.QueryAsync<Product>( "SELECT id, name, price, stock FROM products WHERE price < @MaxPrice ORDER BY price", new { MaxPrice = maxPrice } ); } // --- QueryFirstOrDefaultAsync: single row --- public async Task<Product?> FindByIdAsync(long id) { using var conn = Connection(); return await conn.QueryFirstOrDefaultAsync<Product>( "SELECT id, name, price, stock FROM products WHERE id = @Id", new { Id = id } ); } // --- ExecuteAsync: INSERT / UPDATE / DELETE --- public async Task<int> CreateProductAsync(string name, double price, int stock) { using var conn = Connection(); return await conn.ExecuteAsync( "INSERT INTO products (name, price, stock) VALUES (@Name, @Price, @Stock)", new { Name = name, Price = price, Stock = stock } ); } public async Task<int> UpdatePriceAsync(long id, double newPrice) { using var conn = Connection(); return await conn.ExecuteAsync( "UPDATE products SET price = @Price WHERE id = @Id", new { Price = newPrice, Id = id } ); } // --- Multi-mapping: JOIN two tables into two POCOs --- public async Task<IEnumerable<(Order Order, Customer Customer)>> GetOrdersWithCustomersAsync() { using var conn = Connection(); var results = await conn.QueryAsync<Order, Customer, (Order, Customer)>( @"SELECT o.id, o.customer_id, o.total_amount, o.status, c.id, c.name, c.email FROM orders o JOIN customers c ON c.id = o.customer_id ORDER BY o.created_at DESC", (order, customer) => (order, customer), splitOn: "id" // column name where the second type starts ); return results; } }
ASP.NET Core DI Registration Patterns
Minimal API with Npgsql
csharp// Program.cs — Minimal API pattern var builder = WebApplication.CreateBuilder(args); // Read connection string from appsettings.json string connStr = builder.Configuration.GetConnectionString("WeftKitRel")!; // Register NpgsqlDataSource (thread-safe, connection-pooling built in) builder.Services.AddNpgsqlDataSource(connStr); // Register EF Core alongside if needed builder.Services.AddDbContext<AppDbContext>(opt => opt.UseNpgsql(connStr)); // Register application services builder.Services.AddScoped<OrderService>(); builder.Services.AddScoped<ProductRepository>(); var app = builder.Build(); // Map endpoints app.MapGet("/orders/{id:long}", async (long id, OrderService svc) => { var order = await svc.GetOrderAsync(id); return order is null ? Results.NotFound() : Results.Ok(order); }); app.MapPost("/orders", async (PlaceOrderRequest req, OrderService svc) => { var order = await svc.PlaceOrderAsync(req.CustomerId, req.Amount); return Results.Created($"/orders/{order.Id}", order); }); app.Run(); record PlaceOrderRequest(long CustomerId, decimal Amount);
Controller-Based API with EF Core
csharp// Controllers/OrdersController.cs using Microsoft.AspNetCore.Mvc; [ApiController] [Route("api/[controller]")] public class OrdersController : ControllerBase { private readonly OrderService _orderService; public OrdersController(OrderService orderService) => _orderService = orderService; [HttpGet("{id:long}")] public async Task<IActionResult> Get(long id) { var order = await _orderService.GetOrderAsync(id); return order is null ? NotFound() : Ok(order); } [HttpGet("pending")] public async Task<IActionResult> GetPending() => Ok(await _orderService.GetPendingOrdersAsync()); [HttpPost] public async Task<IActionResult> Create([FromBody] PlaceOrderRequest req) { var order = await _orderService.PlaceOrderAsync(req.CustomerId, req.Amount); return CreatedAtAction(nameof(Get), new { id = order.Id }, order); } [HttpPatch("{id:long}/ship")] public async Task<IActionResult> Ship(long id) { await _orderService.ShipOrderAsync(id); return NoContent(); } }
WeftKitDoc — MongoDB .NET Driver
WeftKitDoc speaks the MongoDB Wire protocol. The standard MongoDB .NET Driver connects directly.
Default port: 27017
Typed POCO and Collection Setup
csharpusing MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver; // --- POCO mapped to a MongoDB document --- [BsonIgnoreExtraElements] public class Product { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } [BsonElement("name")] public string Name { get; set; } = string.Empty; [BsonElement("category")] public string Category { get; set; } = string.Empty; [BsonElement("price")] public double Price { get; set; } [BsonElement("stock")] public int Stock { get; set; } [BsonElement("tags")] public List<string> Tags { get; set; } = new(); }
CRUD Operations
csharpusing MongoDB.Driver; const string connectionString = "mongodb://app_user:secret@localhost:27017/catalog"; var client = new MongoClient(connectionString); var database = client.GetDatabase("catalog"); IMongoCollection<Product> products = database.GetCollection<Product>("products"); // --- InsertOne --- var newProduct = new Product { Name = "Widget Pro", Category = "widgets", Price = 29.99, Stock = 250, Tags = new List<string> { "sale", "featured" } }; await products.InsertOneAsync(newProduct); Console.WriteLine($"Inserted: {newProduct.Id}"); // --- InsertMany --- var batch = new List<Product> { new() { Name = "Gadget A", Category = "gadgets", Price = 9.99, Stock = 100 }, new() { Name = "Gadget B", Category = "gadgets", Price = 14.99, Stock = 50 } }; await products.InsertManyAsync(batch); // --- FindAsync with FilterDefinitionBuilder --- var filter = Builders<Product>.Filter.And( Builders<Product>.Filter.Eq(p => p.Category, "widgets"), Builders<Product>.Filter.Gt(p => p.Price, 10.0) ); var sort = Builders<Product>.Sort.Ascending(p => p.Price); using var cursor = await products.FindAsync(filter, new FindOptions<Product> { Sort = sort, Limit = 20 }); await cursor.ForEachAsync(p => Console.WriteLine($" {p.Name} — ${p.Price:F2}")); // --- UpdateOne --- var updateFilter = Builders<Product>.Filter.Eq(p => p.Name, "Widget Pro"); var update = Builders<Product>.Update .Set(p => p.Price, 24.99) .Inc(p => p.Stock, -10) .AddToSet(p => p.Tags, "clearance"); UpdateResult updateResult = await products.UpdateOneAsync(updateFilter, update); Console.WriteLine($"Matched: {updateResult.MatchedCount}, Modified: {updateResult.ModifiedCount}"); // --- DeleteOne --- DeleteResult deleteResult = await products.DeleteOneAsync( Builders<Product>.Filter.Eq(p => p.Name, "Gadget A")); Console.WriteLine($"Deleted: {deleteResult.DeletedCount}");
Aggregation Pipeline
csharpusing MongoDB.Driver; static async Task SummarizeByCategory(IMongoCollection<Product> products) { var pipeline = products.Aggregate() .Match(Builders<Product>.Filter.Gt(p => p.Stock, 0)) .Group( p => p.Category, g => new { Category = g.Key, TotalStock = g.Sum(p => p.Stock), AvgPrice = g.Average(p => p.Price), Count = g.Count() } ) .SortByDescending(r => r.Count); await pipeline.ForEachAsync(r => Console.WriteLine($"Category: {r.Category} | count={r.Count} | avgPrice=${r.AvgPrice:F2}")); }
WeftKitDoc — ASP.NET Core DI Registration
Configuration Class and appsettings.json
json{ "MongoSettings": { "ConnectionString": "mongodb://app_user:secret@localhost:27017", "DatabaseName": "catalog" } }
csharp// MongoSettings.cs public class MongoSettings { public string ConnectionString { get; set; } = string.Empty; public string DatabaseName { get; set; } = string.Empty; }
Registration and Injection
csharp// Program.cs builder.Services.Configure<MongoSettings>( builder.Configuration.GetSection("MongoSettings")); builder.Services.AddSingleton<IMongoClient>(sp => { var settings = sp.GetRequiredService<IOptions<MongoSettings>>().Value; return new MongoClient(settings.ConnectionString); }); builder.Services.AddScoped<IMongoDatabase>(sp => { var settings = sp.GetRequiredService<IOptions<MongoSettings>>().Value; var client = sp.GetRequiredService<IMongoClient>(); return client.GetDatabase(settings.DatabaseName); }); builder.Services.AddScoped<ProductRepository>();
csharp// ProductRepository.cs — injected via DI using Microsoft.Extensions.Options; using MongoDB.Driver; public class ProductRepository { private readonly IMongoCollection<Product> _collection; public ProductRepository(IMongoDatabase database) { _collection = database.GetCollection<Product>("products"); } public async Task<List<Product>> GetByCategoryAsync(string category) => await _collection .Find(Builders<Product>.Filter.Eq(p => p.Category, category)) .ToListAsync(); public async Task<Product?> GetByIdAsync(string id) => await _collection .Find(Builders<Product>.Filter.Eq(p => p.Id, id)) .FirstOrDefaultAsync(); public async Task CreateAsync(Product product) => await _collection.InsertOneAsync(product); }
WeftKitMem — StackExchange.Redis
WeftKitMem speaks the Redis RESP3 protocol. StackExchange.Redis is the standard high-performance .NET client.
Default port: 6379
Connection Setup
csharpusing StackExchange.Redis; // Create once and share — ConnectionMultiplexer is thread-safe var configOptions = new ConfigurationOptions { EndPoints = { { "localhost", 6379 } }, Password = "secret", AbortOnConnectFail = false, ConnectRetry = 3, ReconnectRetryPolicy = new ExponentialRetry(5000) }; IConnectionMultiplexer multiplexer = await ConnectionMultiplexer.ConnectAsync(configOptions); IDatabase db = multiplexer.GetDatabase(0);
String, Hash, List, and Sorted Set Operations
csharp// --- String (value) operations --- await db.StringSetAsync("session:abc123", "user-id:42", TimeSpan.FromHours(1)); RedisValue sessionValue = await db.StringGetAsync("session:abc123"); Console.WriteLine($"Session: {sessionValue}"); bool existed = await db.KeyDeleteAsync("session:abc123"); // Atomic increment long views = await db.StringIncrementAsync("stats:page_views"); // --- Hash operations --- await db.HashSetAsync("user:42", new HashEntry[] { new("name", "Alice"), new("email", "alice@example.com"), new("role", "admin") }); RedisValue email = await db.HashGetAsync("user:42", "email"); HashEntry[] allFields = await db.HashGetAllAsync("user:42"); Console.WriteLine($"Email: {email}"); // --- List operations --- await db.ListLeftPushAsync("queue:jobs", "job-001"); await db.ListLeftPushAsync("queue:jobs", "job-002"); RedisValue[] jobs = await db.ListRangeAsync("queue:jobs", 0, -1); long queueLength = await db.ListLengthAsync("queue:jobs"); // --- Sorted Set operations --- await db.SortedSetAddAsync("leaderboard", new SortedSetEntry[] { new("alice", 1500), new("bob", 1250), new("carol", 1750) }); SortedSetEntry[] topPlayers = await db.SortedSetRangeByRankWithScoresAsync( "leaderboard", 0, 9, Order.Descending); foreach (var entry in topPlayers) Console.WriteLine($" {entry.Element}: {entry.Score}");
Pub/Sub
csharpISubscriber subscriber = multiplexer.GetSubscriber(); // Subscribe to a channel await subscriber.SubscribeAsync(RedisChannel.Literal("notifications:orders"), (channel, message) => { Console.WriteLine($"Received on {channel}: {message}"); }); // Publish a message from another part of the application await subscriber.PublishAsync(RedisChannel.Literal("notifications:orders"), "order-shipped:99");
WeftKitMem — ASP.NET Core DI Registration
appsettings.json
json{ "Redis": { "ConnectionString": "localhost:6379,password=secret,abortConnect=false" } }
Registration
csharp// Program.cs builder.Services.AddSingleton<IConnectionMultiplexer>(_ => ConnectionMultiplexer.Connect( builder.Configuration["Redis:ConnectionString"]!)); // Expose IDatabase as a scoped service builder.Services.AddScoped<IDatabase>(sp => sp.GetRequiredService<IConnectionMultiplexer>().GetDatabase(0)); builder.Services.AddScoped<CacheService>();
csharp// CacheService.cs using StackExchange.Redis; public class CacheService { private readonly IDatabase _db; public CacheService(IDatabase db) => _db = db; public async Task SetAsync<T>(string key, T value, TimeSpan expiry) { string json = System.Text.Json.JsonSerializer.Serialize(value); await _db.StringSetAsync(key, json, expiry); } public async Task<T?> GetAsync<T>(string key) { RedisValue value = await _db.StringGetAsync(key); if (value.IsNullOrEmpty) return default; return System.Text.Json.JsonSerializer.Deserialize<T>(value!); } public async Task InvalidateAsync(string key) => await _db.KeyDeleteAsync(key); }
WeftKitGraph — Neo4j .NET Driver
WeftKitGraph speaks the Bolt protocol. The standard Neo4j .NET Driver connects without modification.
Default port: 7687
Driver Setup and Session Usage
csharpusing Neo4j.Driver; // Create once and share across the application lifetime IDriver driver = GraphDatabase.Driver( "bolt://localhost:7687", AuthTokens.Basic("neo4j", "secret"), config => config.WithMaxConnectionPoolSize(50)); // Verify connectivity at startup await driver.VerifyConnectivityAsync();
Write and Read Transactions
csharpusing Neo4j.Driver; // --- Write: create nodes and relationships --- static async Task CreateUserFollowsAsync(IDriver driver, string fromName, string toName) { await using IAsyncSession session = driver.AsyncSession(); await session.ExecuteWriteAsync(async tx => { await tx.RunAsync( "MERGE (a:User {name: $from}) " + "MERGE (b:User {name: $to}) " + "MERGE (a)-[:FOLLOWS]->(b)", new { from = fromName, to = toName }); }); } // --- Read: find followers --- static async Task<List<string>> GetFollowersAsync(IDriver driver, string userName) { await using IAsyncSession session = driver.AsyncSession(); return await session.ExecuteReadAsync(async tx => { var cursor = await tx.RunAsync( "MATCH (follower:User)-[:FOLLOWS]->(u:User {name: $name}) " + "RETURN follower.name AS followerName " + "ORDER BY followerName", new { name = userName }); var records = await cursor.ToListAsync(); return records.Select(r => r["followerName"].As<string>()).ToList(); }); } // --- Read: shortest path --- static async Task<int> ShortestPathLengthAsync(IDriver driver, string fromName, string toName) { await using IAsyncSession session = driver.AsyncSession(); return await session.ExecuteReadAsync(async tx => { var cursor = await tx.RunAsync( "MATCH p = shortestPath(" + " (a:User {name: $from})-[:FOLLOWS*]-(b:User {name: $to})" + ") RETURN length(p) AS hops", new { from = fromName, to = toName }); return await cursor.PeekAsync() is not null ? (await cursor.SingleAsync())["hops"].As<int>() : -1; }); } // --- Write: remove a relationship --- static async Task UnfollowAsync(IDriver driver, string fromName, string toName) { await using IAsyncSession session = driver.AsyncSession(); await session.ExecuteWriteAsync(async tx => { var cursor = await tx.RunAsync( "MATCH (a:User {name: $from})-[r:FOLLOWS]->(b:User {name: $to}) DELETE r", new { from = fromName, to = toName }); var summary = await cursor.ConsumeAsync(); Console.WriteLine($"Relationships deleted: {summary.Counters.RelationshipsDeleted}"); }); } // Example usage await CreateUserFollowsAsync(driver, "Alice", "Bob"); await CreateUserFollowsAsync(driver, "Alice", "Carol"); await CreateUserFollowsAsync(driver, "Bob", "Carol"); var followers = await GetFollowersAsync(driver, "Carol"); Console.WriteLine($"Carol's followers: {string.Join(", ", followers)}"); int hops = await ShortestPathLengthAsync(driver, "Alice", "Carol"); Console.WriteLine($"Shortest path Alice -> Carol: {hops} hop(s)"); // Dispose driver when the application shuts down await driver.DisposeAsync();
ASP.NET Core DI Registration
csharp// Program.cs builder.Services.AddSingleton<IDriver>(_ => GraphDatabase.Driver( "bolt://localhost:7687", AuthTokens.Basic("neo4j", "secret"))); // Expose a scoped session factory builder.Services.AddScoped(sp => sp.GetRequiredService<IDriver>().AsyncSession()); builder.Services.AddScoped<GraphService>();
csharp// GraphService.cs public class GraphService { private readonly IDriver _driver; public GraphService(IDriver driver) => _driver = driver; public async Task<List<string>> GetFollowersAsync(string userName) { await using var session = _driver.AsyncSession(); return await session.ExecuteReadAsync(async tx => { var cursor = await tx.RunAsync( "MATCH (f:User)-[:FOLLOWS]->(u:User {name: $name}) RETURN f.name AS name", new { name = userName }); return (await cursor.ToListAsync()) .Select(r => r["name"].As<string>()) .ToList(); }); } }
WeftKitKV — AWS SDK for .NET
WeftKitKV exposes a DynamoDB REST API. Point the AWS SDK at WeftKit's endpoint instead of AWS.
Default port: 8000
Client Setup and Low-Level Item Operations
csharpusing Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using Amazon.Runtime; // Point the SDK at WeftKit instead of AWS var config = new AmazonDynamoDBConfig { ServiceURL = "http://localhost:8000" }; var credentials = new BasicAWSCredentials("weftkit", "secret"); var dynamo = new AmazonDynamoDBClient(credentials, config); // --- PutItem (INSERT or full replace) --- static async Task PutProductAsync(IAmazonDynamoDB dynamo, string productId, string name, double price, int stock) { await dynamo.PutItemAsync(new PutItemRequest { TableName = "products", Item = new Dictionary<string, AttributeValue> { ["productId"] = new AttributeValue { S = productId }, ["name"] = new AttributeValue { S = name }, ["price"] = new AttributeValue { N = price.ToString() }, ["stock"] = new AttributeValue { N = stock.ToString() } } }); Console.WriteLine($"PutItem succeeded for: {productId}"); } // --- GetItem --- static async Task<Dictionary<string, AttributeValue>?> GetProductAsync( IAmazonDynamoDB dynamo, string productId) { var response = await dynamo.GetItemAsync(new GetItemRequest { TableName = "products", Key = new Dictionary<string, AttributeValue> { ["productId"] = new AttributeValue { S = productId } } }); if (!response.IsItemSet) { Console.WriteLine($"Item not found: {productId}"); return null; } Console.WriteLine($"Found: {response.Item["name"].S} | " + $"price={response.Item["price"].N} | " + $"stock={response.Item["stock"].N}"); return response.Item; } // --- UpdateItem (partial update with condition) --- static async Task DecrementStockAsync(IAmazonDynamoDB dynamo, string productId, int quantity) { await dynamo.UpdateItemAsync(new UpdateItemRequest { TableName = "products", Key = new Dictionary<string, AttributeValue> { ["productId"] = new AttributeValue { S = productId } }, UpdateExpression = "SET stock = stock - :qty", ConditionExpression = "stock >= :qty", ExpressionAttributeValues = new Dictionary<string, AttributeValue> { [":qty"] = new AttributeValue { N = quantity.ToString() } } }); } // --- DeleteItem --- static async Task DeleteProductAsync(IAmazonDynamoDB dynamo, string productId) { await dynamo.DeleteItemAsync(new DeleteItemRequest { TableName = "products", Key = new Dictionary<string, AttributeValue> { ["productId"] = new AttributeValue { S = productId } } }); Console.WriteLine($"Deleted: {productId}"); } // --- Query with KeyConditionExpression --- static async Task<List<Dictionary<string, AttributeValue>>> QueryByCategoryAsync( IAmazonDynamoDB dynamo, string category) { var response = await dynamo.QueryAsync(new QueryRequest { TableName = "products", KeyConditionExpression = "category = :cat", ExpressionAttributeValues = new Dictionary<string, AttributeValue> { [":cat"] = new AttributeValue { S = category } }, Limit = 100 }); Console.WriteLine($"Items found: {response.Count}"); return response.Items; }
High-Level DynamoDBContext with Attributes
csharpusing Amazon.DynamoDBv2; using Amazon.DynamoDBv2.DataModel; // --- POCO annotated for the high-level client --- [DynamoDBTable("products")] public class ProductItem { [DynamoDBHashKey("productId")] public string ProductId { get; set; } = string.Empty; [DynamoDBProperty("name")] public string Name { get; set; } = string.Empty; [DynamoDBProperty("price")] public double Price { get; set; } [DynamoDBProperty("stock")] public int Stock { get; set; } } // --- Usage with DynamoDBContext --- static async Task HighLevelExampleAsync(IAmazonDynamoDB dynamo) { var context = new DynamoDBContext(dynamo); // Save (upsert) var product = new ProductItem { ProductId = "prod-001", Name = "Widget Pro", Price = 29.99, Stock = 250 }; await context.SaveAsync(product); // Load by primary key var loaded = await context.LoadAsync<ProductItem>("prod-001"); Console.WriteLine($"Loaded: {loaded?.Name}"); // Update and save if (loaded is not null) { loaded.Price = 24.99; await context.SaveAsync(loaded); } // Delete await context.DeleteAsync<ProductItem>("prod-001"); }
ASP.NET Core DI Registration
csharp// Program.cs builder.Services.AddSingleton<IAmazonDynamoDB>(_ => { var config = new AmazonDynamoDBConfig { ServiceURL = builder.Configuration["WeftKitKV:Endpoint"] ?? "http://localhost:8000" }; var creds = new BasicAWSCredentials("weftkit", "secret"); return new AmazonDynamoDBClient(creds, config); }); builder.Services.AddScoped<IDynamoDBContext>(sp => new DynamoDBContext(sp.GetRequiredService<IAmazonDynamoDB>()));
json// appsettings.json { "WeftKitKV": { "Endpoint": "http://localhost:8000" } }
Connection String Reference
| Engine | Driver | Connection Detail |
|---|---|---|
| WeftKitRel | Npgsql / EF Core / Dapper | Host=localhost;Port=5432;Database=mydb;Username=app_user;Password=secret |
| WeftKitDoc | MongoDB .NET Driver | mongodb://app_user:secret@localhost:27017/catalog |
| WeftKitMem | StackExchange.Redis | localhost:6379,password=secret |
| WeftKitGraph | Neo4j .NET Driver | bolt://localhost:7687 |
| WeftKitKV | AWS SDK for .NET | ServiceURL = "http://localhost:8000" |
All ports are configurable in weftkit.toml. See the Integration Guides overview for TLS/SSL and authentication details.
On this page