Nedo.AspNet.Infrastructure
1.0.9
dotnet add package Nedo.AspNet.Infrastructure --version 1.0.9
NuGet\Install-Package Nedo.AspNet.Infrastructure -Version 1.0.9
<PackageReference Include="Nedo.AspNet.Infrastructure" Version="1.0.9" />
<PackageVersion Include="Nedo.AspNet.Infrastructure" Version="1.0.9" />
<PackageReference Include="Nedo.AspNet.Infrastructure" />
paket add Nedo.AspNet.Infrastructure --version 1.0.9
#r "nuget: Nedo.AspNet.Infrastructure, 1.0.9"
#:package Nedo.AspNet.Infrastructure@1.0.9
#addin nuget:?package=Nedo.AspNet.Infrastructure&version=1.0.9
#tool nuget:?package=Nedo.AspNet.Infrastructure&version=1.0.9
Nedo.AspNet.Infrastructure
A modular, production-ready infrastructure library for .NET 9 that standardizes integration with databases, message brokers, caches, and background services.
Features
- ๐ Unified Connectivity โ Standardized patterns for database, messaging, and caching
- ๐๏ธ Multi-Database โ PostgreSQL, Oracle, MySQL, MariaDB, SQLite, InfluxDB, Qdrant
- ๐จ Multi-Broker Messaging โ RabbitMQ, NATS JetStream, MQTT, Redis Streams
- ๐พ Distributed Caching โ In-Memory, Redis, Valkey
- ๐ Health Checks โ Fail-fast startup + HTTP liveness/readiness probes
- โ๏ธ Background Processing โ Work queues and scheduled jobs
- ๐ Observability โ
ActivitySourcetracing +System.Diagnostics.Metrics - ๐๏ธ Config Validation โ Catch configuration errors at startup, not at 3 AM
- โ Testable โ Interface-driven, easy to mock
Quick Start
dotnet add package Nedo.AspNet.Infrastructure
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddNedoInfrastructure(builder.Configuration, infra =>
{
// Database
infra.AddPostgreSQL();
// Messaging (pick one)
infra.AddRabbitMQ(msg =>
{
msg.AddConsumer<OrderCreated, OrderCreatedHandler>("orders.created");
});
// Caching (pick one)
infra.AddRedisCache();
});
var app = builder.Build();
await app.RunStartupHealthChecksAsync(); // fail-fast if services unreachable
app.MapInfrastructureHealthChecks(); // HTTP health endpoints
app.Run();
Database
Providers
| Provider | Extension | EF Core | Package |
|---|---|---|---|
| PostgreSQL | AddPostgreSQL() |
AddPostgreSqlDbContext<T>() |
Npgsql |
| Oracle | AddOracle() |
AddOracleDbContext<T>() |
Oracle.ManagedDataAccess |
| MySQL | AddMySQL() |
AddMySqlDbContext<T>() |
MySqlConnector |
| MariaDB | AddMariaDb() |
AddMariaDbDbContext<T>() |
MySqlConnector |
| SQLite | AddSQLite() |
AddSqliteDbContext<T>() |
Microsoft.Data.Sqlite |
| InfluxDB | AddInfluxDb() |
โ (time-series) | InfluxDB.Client |
| Qdrant | AddQdrant() |
โ (vector DB) | Qdrant.Client |
Configuration
{
"Infrastructure": {
"Database": {
"Provider": "PostgreSQL",
"Connection": {
"Host": "localhost",
"Port": 5432,
"Database": "mydb",
"Username": "user",
"Password": "pass"
},
"CommandTimeout": 30
}
}
}
If both
ConnectionStringandConnection.*are set,ConnectionStringtakes priority.
Qdrant (Vector Database)
infra.AddQdrant(opts =>
{
opts.Host = "localhost";
opts.Port = 6334;
});
public class EmbeddingService(IQdrantVectorClient qdrant)
{
public async Task IndexAsync(string id, float[] embedding)
{
var point = new PointStruct
{
Id = new PointId { Uuid = id },
Vectors = embedding,
Payload = { ["indexed_at"] = DateTimeOffset.UtcNow.ToString("O") }
};
await qdrant.UpsertAsync("documents", [point]);
}
public async Task<IReadOnlyList<ScoredPoint>> SearchAsync(float[] query)
{
return await qdrant.SearchAsync("documents", query, limit: 5);
}
}
<details> <summary>Qdrant Configuration</summary>
{
"Infrastructure": {
"Database": {
"Qdrant": {
"Host": "localhost",
"Port": 6334,
"Https": false,
"ApiKey": "",
"DefaultCollection": "",
"Timeout": "00:00:10"
}
}
}
}
</details>
Messaging
Providers
| Provider | Extension | Package | Best For |
|---|---|---|---|
| RabbitMQ | AddRabbitMQ() |
RabbitMQ.Client |
Enterprise messaging, task queues |
| NATS JetStream | AddNatsJetStream() |
NATS.Net |
High-throughput streaming |
| MQTT | AddMqtt() |
MQTTnet |
IoT, lightweight pub/sub |
| Redis Streams | AddRedisStream() |
StackExchange.Redis |
Persistent log, consumer groups |
Usage
Publish messages:
public class OrderService(IMessagePublisher publisher)
{
public async Task CreateOrderAsync(Order order)
{
await _repo.SaveAsync(order);
await publisher.PublishAsync(
new OrderCreated(order.Id, order.Total),
topic: "orders.created",
properties: new MessageProperties
{
CorrelationId = Activity.Current?.Id,
DeliveryMode = DeliveryMode.Persistent
});
}
}
Consume messages:
public record OrderCreated(Guid OrderId, decimal Total);
public class OrderCreatedHandler(ILogger<OrderCreatedHandler> logger)
: IMessageConsumer<OrderCreated>
{
public async Task<bool> HandleAsync(
ConsumeContext<OrderCreated> context, CancellationToken ct = default)
{
logger.LogInformation("Order {Id}: ${Total}",
context.Message.OrderId, context.Message.Total);
// Process the order...
return true; // ack
// return false; โ nack / requeue
}
}
Register consumers:
infra.AddRabbitMQ(msg =>
{
msg.AddConsumer<OrderCreated, OrderCreatedHandler>("orders.created");
msg.AddConsumer<PaymentReceived, PaymentHandler>("payments.received");
});
// Or with NATS JetStream:
infra.AddNatsJetStream(msg =>
{
msg.AddConsumer<OrderCreated, OrderCreatedHandler>("APP.orders.created");
});
// Or with MQTT:
infra.AddMqtt(msg =>
{
msg.AddConsumer<SensorReading, SensorHandler>("sensors/temperature");
});
// Or with Redis Streams:
infra.AddRedisStream(msg =>
{
msg.AddConsumer<OrderCreated, OrderCreatedHandler>("orders.created");
});
Configuration
<details> <summary>RabbitMQ</summary>
{
"Infrastructure": {
"Messaging": {
"Enabled": true,
"Provider": "RabbitMQ",
"HostName": "localhost",
"Port": 5672,
"Username": "guest",
"Password": "guest",
"Exchange": { "Name": "app.events", "Type": "topic", "Durable": true },
"Consumer": { "PrefetchCount": 10, "RetryCount": 3 },
"DeadLetter": { "Enabled": true, "Exchange": "app.deadletter", "MaxRetries": 5 }
}
}
}
</details>
<details> <summary>NATS JetStream</summary>
{
"Infrastructure": {
"Messaging": {
"Enabled": true,
"Provider": "NatsJetStream",
"Nats": {
"Url": "nats://localhost:4222",
"StreamName": "APP",
"DurableName": "app-worker",
"AckWait": "00:00:30",
"MaxDeliver": 5
}
}
}
}
</details>
<details> <summary>MQTT</summary>
{
"Infrastructure": {
"Messaging": {
"Enabled": true,
"Provider": "MQTT",
"Mqtt": {
"BrokerHost": "localhost",
"BrokerPort": 1883,
"ClientId": "myapp-worker",
"CleanSession": true,
"DefaultQoS": 1,
"UseTls": false
}
}
}
}
</details>
<details> <summary>Redis Streams</summary>
{
"Infrastructure": {
"Messaging": {
"Enabled": true,
"Provider": "RedisStream",
"RedisStream": {
"ConnectionString": "localhost:6379",
"StreamPrefix": "messaging",
"ConsumerGroup": "app-workers",
"ConsumerName": "worker-1",
"ReadBatchSize": 10,
"MaxStreamLength": 0
}
}
}
}
</details>
Caching
Providers
| Provider | Extension | Package |
|---|---|---|
| In-Memory | AddInMemoryCache() |
(built-in) |
| Redis | AddRedisCache() |
StackExchange.Redis |
| Valkey | AddValkeyCache() |
StackExchange.Redis |
| Hybrid | AddHybridCache() |
L1 in-memory + L2 distributed |
Usage
public class ProductService(ICacheProvider cache, IProductRepo repo)
{
public async Task<Product?> GetAsync(int id, CancellationToken ct)
{
return await cache.GetOrCreateAsync(
key: $"products:{id}",
factory: async ct => await repo.FindByIdAsync(id, ct),
options: new CacheEntryOptions
{
AbsoluteExpiration = TimeSpan.FromMinutes(30),
Tags = ["products"]
},
ct: ct);
}
public async Task InvalidateCategoryAsync(CancellationToken ct)
{
await cache.RemoveByTagAsync("products", ct);
}
}
Configuration
{
"Infrastructure": {
"Cache": {
"Enabled": true,
"ConnectionString": "localhost:6379",
"KeyPrefix": "myapp:",
"DefaultExpiration": "01:00:00"
}
}
}
Health Checks
var app = builder.Build();
await app.RunStartupHealthChecksAsync(); // fail-fast if DB/cache/broker unreachable
app.MapInfrastructureHealthChecks(); // HTTP endpoints
| Endpoint | Tag | Purpose |
|---|---|---|
GET /health/live |
Liveness | Is the process alive? |
GET /health/ready |
Readiness | Can it handle traffic? |
GET /health/startup |
Startup | Has initial setup completed? |
All registered providers (database, messaging, caching) automatically contribute health checks.
Observability
Every module emits structured tracing and metrics:
| Module | Trace Span | Metrics |
|---|---|---|
| Database | db.query, db.connection |
infra.db.queries.* |
| Messaging | messaging.publish, messaging.consume |
infra.messaging.* |
| Cache | cache.get, cache.set, cache.remove |
infra.cache.* |
Documentation
Full documentation in docs/:
| # | Section | Documents |
|---|---|---|
| 00 | ๐ | Getting Started |
| 01 | ๐๏ธ Database | Overview ยท Connection Strings ยท PostgreSQL ยท Oracle ยท MySQL ยท MariaDB ยท SQLite ยท InfluxDB ยท Qdrant ยท EF Core |
| 02 | ๐จ Messaging | Overview ยท RabbitMQ ยท NATS JetStream ยท MQTT ยท Redis Streams |
| 03 | ๐พ Cache | Overview ยท Usage Patterns ยท Redis ยท Valkey ยท In-Memory ยท Hybrid ยท Configuration |
| 04 | ๐ Health | Overview |
| 05 | ๐ Observability | Overview |
| 06 | โ๏ธ Background | Overview ยท Work Queues ยท Scheduled Jobs ยท Managed Services |
| 07 | ๐ง Core | Configuration ยท Connection Lifecycle ยท Resilience ยท Extensibility |
Project Structure
โโโ docs/ # Documentation (by topic)
โโโ sample/ # Sample projects
โโโ src/Nedo.AspNet.Infrastructure/
โ โโโ Builder/ # DI builder & extensions
โ โโโ Configuration/ # Strongly-typed options
โ โโโ Core/ # Connection factory & pooling
โ โโโ Database/ # PostgreSQL, Oracle, MySQL, MariaDB, SQLite
โ โ โโโ InfluxDb/ # Time-series database
โ โ โโโ Qdrant/ # Vector database
โ โโโ Messaging/
โ โ โโโ RabbitMQ/ # AMQP provider
โ โ โโโ NatsJetStream/ # NATS provider
โ โ โโโ Mqtt/ # MQTT provider
โ โ โโโ RedisStream/ # Redis Streams provider
โ โโโ Caching/
โ โ โโโ Redis/ # Redis/Valkey provider
โ โโโ HealthChecks/
โ โโโ Observability/
โ โโโ BackgroundProcessing/
โโโ test/ # Unit tests (102 passing)
โโโ .gitlab-ci.yml
โโโ README.md
Build & Test
dotnet build
dotnet test # 102 tests
Releasing
- Tag:
git tag v1.0.0 - Push:
git push origin v1.0.0 - GitLab CI builds, packs, and publishes to NuGet.
Requires:
NUGET_API_KEYCI/CD variable.
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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 was computed. 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. |
-
net9.0
- Dapper (>= 2.1.66)
- InfluxDB.Client (>= 5.0.0)
- Microsoft.Data.Sqlite (>= 10.0.3)
- Microsoft.EntityFrameworkCore.Sqlite (>= 9.0.2)
- MQTTnet (>= 4.3.7.1207)
- MySqlConnector (>= 2.5.0)
- NATS.Net (>= 2.7.2)
- Npgsql (>= 9.0.3)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 9.0.4)
- Oracle.EntityFrameworkCore (>= 9.23.80)
- Oracle.ManagedDataAccess.Core (>= 23.26.100)
- Pomelo.EntityFrameworkCore.MySql (>= 9.0.0)
- Qdrant.Client (>= 1.17.0)
- RabbitMQ.Client (>= 7.2.0)
- StackExchange.Redis (>= 2.11.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.