TAF.CRUD.SDK
6.0.8
dotnet add package TAF.CRUD.SDK --version 6.0.8
NuGet\Install-Package TAF.CRUD.SDK -Version 6.0.8
<PackageReference Include="TAF.CRUD.SDK" Version="6.0.8" />
<PackageVersion Include="TAF.CRUD.SDK" Version="6.0.8" />
<PackageReference Include="TAF.CRUD.SDK" />
paket add TAF.CRUD.SDK --version 6.0.8
#r "nuget: TAF.CRUD.SDK, 6.0.8"
#:package TAF.CRUD.SDK@6.0.8
#addin nuget:?package=TAF.CRUD.SDK&version=6.0.8
#tool nuget:?package=TAF.CRUD.SDK&version=6.0.8
TAF.CRUD.SDK
Official .NET SDK for consuming TAF.CRUD microservice. Provides both synchronous and asynchronous CRUD operations with a fluent query builder API.
Installation
dotnet add package TAF.CRUD.SDK
Features
- Dual Client Modes: Synchronous (wait for results) or asynchronous (fire-and-forget with RabbitMQ events)
- Fluent Query Builder: Strongly-typed filter conditions with all SQL operators
- Automatic Retries: Configurable retry logic for failed requests (sync mode only)
- Event Carried State Transfer: Pass metadata through Context.Properties to consuming microservices
- Multi-Tenant Support: Built-in context headers for tenant isolation
- Self-Contained Package: All dependencies bundled - install one package, get everything
Quick Start
Configuration
Register both clients with a single method:
using TAF.CRUD.SDK.Extensions;
// Register both Sync and Async clients
builder.Services.AddCRUDClient(
// Sync client (with retry options)
syncOptions =>
{
syncOptions.BaseUrl = "https://api.example.com";
syncOptions.TimeoutSeconds = 30;
syncOptions.MaxRetryAttempts = 3;
syncOptions.RetryDelayMs = 1000;
syncOptions.ApiKey = "your-api-key";
},
// Async client (fire-and-forget)
asyncOptions =>
{
asyncOptions.BaseUrl = "https://api.example.com";
asyncOptions.TimeoutSeconds = 30;
asyncOptions.ApiKey = "your-api-key";
}
);
// Or register only Sync client
builder.Services.AddCRUDClient(
syncOptions => { syncOptions.BaseUrl = "https://api.example.com"; }
);
// Or register only Async client
builder.Services.AddCRUDClient(
asyncConfigure: asyncOptions => { asyncOptions.BaseUrl = "https://api.example.com"; }
);
Synchronous Client Usage
Use for traditional request-response operations:
public class OrderService
{
private readonly SyncCRUDClient _client;
public OrderService(SyncCRUDClient client)
{
_client = client;
}
public async Task<Guid> CreateOrderAsync(string orderNumber, decimal amount)
{
var context = new Context
{
TenantId = Guid.Parse("tenant-id"),
AppId = Guid.Parse("app-id"),
EnvironmentId = Guid.Parse("env-id"),
UserId = Guid.Parse("user-id")
};
var orderId = await _client
.Query("Orders")
.Insert(new Dictionary<string, object>
{
["OrderNumber"] = orderNumber,
["Amount"] = amount,
["OrderDate"] = DateTime.UtcNow
})
.WithContext(context)
.ExecuteAsync();
return orderId; // Actual GUID returned immediately
}
public async Task<List<Dictionary<string, object>>> GetActiveOrdersAsync()
{
return await _client
.Query("Orders")
.Where(filter => filter
.And(c => c.Field("IsActive").EqualTo(true))
.And(c => c.Field("Amount").GreaterThan(100)))
.WithContext(context)
.ToListAsync();
}
public async Task<int> UpdateOrderAsync(Guid orderId, string status)
{
return await _client
.Query("Orders")
.Where(filter => filter.And(c => c.Field("Id").EqualTo(orderId)))
.WithContext(context)
.UpdateAsync(new Dictionary<string, object>
{
["Status"] = status,
["UpdatedDate"] = DateTime.UtcNow
});
}
public async Task<int> DeleteOldOrdersAsync()
{
return await _client
.Query("Orders")
.Where(filter => filter
.And(c => c.Field("OrderDate").LessThan(DateTime.UtcNow.AddYears(-1))))
.WithContext(context)
.DeleteAsync();
}
}
Asynchronous Client Usage
Use for microservice-to-microservice communication with event-driven results:
public class AsyncOrderService
{
private readonly AsyncCRUDClient _client;
public AsyncOrderService(AsyncCRUDClient client)
{
_client = client;
}
public async Task<Guid> CreateOrderAsync(string orderNumber, decimal amount)
{
var context = new Context
{
TenantId = Guid.Parse("tenant-id"),
AppId = Guid.Parse("app-id"),
EnvironmentId = Guid.Parse("env-id"),
UserId = Guid.Parse("user-id"),
// Pass metadata to event consumers
Properties = new Dictionary<string, object>
{
["OrderType"] = "StandardOrder",
["Source"] = "OrderService"
}
};
// Fire-and-forget: Returns CorrelationId immediately
var correlationId = await _client
.Query("Orders")
.Insert(new Dictionary<string, object>
{
["OrderNumber"] = orderNumber,
["Amount"] = amount
})
.WithContext(context)
.ExecuteAsync();
// Actual result arrives via RabbitMQ RecordInsertedEvent
return correlationId;
}
}
Event Consumption
Consume RabbitMQ events to get operation results:
using TAF.CRUD.Domain.Events;
using MassTransit;
public class OrderInsertedEventConsumer : IConsumer<RecordInsertedEvent>
{
public async Task Consume(ConsumeContext<RecordInsertedEvent> context)
{
var @event = context.Message;
if (@event.EntityName != "Orders") return;
// Access custom metadata
var orderType = @event.Properties.GetValueOrDefault("OrderType")?.ToString();
if (@event.IsSuccess)
{
// @event.RecordId contains the new order's GUID
// @event.Properties contains your custom metadata
// Process successful creation
}
else
{
// Handle failure: @event.ErrorMessage, @event.ErrorCode
}
}
}
Fluent Query API
Filtering
// Simple conditions
.Where(filter => filter.And(c => c.Field("Status").EqualTo("Active")))
// Multiple conditions
.Where(filter => filter
.And(c => c.Field("Status").EqualTo("Active"))
.And(c => c.Field("Amount").GreaterThan(1000))
.Or(c => c.Field("Priority").EqualTo("High")))
// Grouped conditions
.Where(filter => filter
.And(c => c.Field("IsActive").EqualTo(true))
.AndGroup(nested => nested
.And(c => c.Field("Status").EqualTo("Pending"))
.Or(c => c.Field("Status").EqualTo("InProgress"))))
// Conditional filtering
.Where(filter => filter
.And(c => c.Field("IsActive").EqualTo(true))
.AndIf(includeExpensive, c => c.Field("Price").GreaterThan(1000)))
Supported Operators
Comparison: EqualTo, NotEqualTo, GreaterThan, GreaterThanOrEqualTo, LessThan, LessThanOrEqualTo
String: Contains, NotContains, StartsWith, NotStartsWith, EndsWith, NotEndsWith
List: In, NotIn
Range: Between, NotBetween
Null: IsNull, IsNotNull
Field Selection & Ordering
// Select specific fields
.Select("OrderNumber", "Amount", "OrderDate")
// Ordering
.OrderBy("OrderDate")
.OrderByDescending("Amount")
// Single record
.FirstOrDefaultAsync()
Architecture
Unified Registration
The SDK uses a single registration method that supports three scenarios:
// Both clients
services.AddCRUDClient(syncOptions => {...}, asyncOptions => {...});
// Sync only
services.AddCRUDClient(syncOptions => {...});
// Async only
services.AddCRUDClient(asyncConfigure: asyncOptions => {...});
Benefits:
- ✅ 55% less code (70 lines vs 155 lines)
- ✅ Optional parameters - register only what you need
- ✅ Core services registered once and shared
- ✅ Sync client preferred as default for
IQueryRepository
Core Services (SOLID Principles)
IUrlBuilder (CRUDUrlBuilder)
Constructs endpoint URLs for all CRUD operations. Singleton lifetime.
string BuildSyncInsertUrl();
string BuildAsyncInsertUrl(); // CorrelationId in header, not URL
string BuildSelectUrl();
IHttpRequestFactory (CRUDHttpRequestFactory)
Creates HTTP requests and converts Context to headers. Singleton lifetime.
HttpRequestMessage CreatePostRequest<TRequest>(string url, TRequest body, Context context);
Context Headers (aligned with TAF.Infra.MassTransit):
TenantIdfromcontext.TenantIdAppIdfromcontext.AppIdEnvironmentIdfromcontext.EnvironmentIdUserIdfromcontext.UserIdCorrelationIdfromcontext.CorrelationId
IResponseProcessor (CRUDResponseProcessor)
Response deserialization, error handling, and validation. Singleton lifetime.
Task<OperationResult<T>> ProcessResponseAsync<T>(HttpResponseMessage response);
Task EnsureSuccessAsync(HttpResponseMessage response, string operation);
Client Comparison
| Feature | SyncCRUDClient | AsyncCRUDClient |
|---|---|---|
| Pattern | Request → Response | Fire-and-forget → Events |
| Returns | Actual results | CorrelationId |
| Retry Logic | ✅ Yes | ❌ No |
| Use Case | Traditional APIs | Microservice communication |
| Event Consumption | ❌ Not needed | ✅ Required |
| Performance | Waits for completion | Returns immediately |
Self-Contained Packaging
The package includes all dependencies:
- TAF.CRUD.SDK.dll
- TAF.CRUD.Contracts.dll
- TAF.CRUD.Domain.dll
- TAF.CRUD.SharedKernel.dll
- TAF.QueryBuilder.Abstractions.dll
External NuGet dependencies (auto-resolved):
- Microsoft.Extensions.Http (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
- TAF.Infra.MassTransit (>= 1.0.4)
Benefit: Install one package, get everything needed. No version conflicts.
Configuration Options
SyncCRUDClientOptions
| Property | Type | Default | Description |
|---|---|---|---|
| BaseUrl | string | required | CRUD API base URL |
| TimeoutSeconds | int | 30 | HTTP timeout |
| MaxRetryAttempts | int | 3 | Retry attempts |
| RetryDelayMs | int | 1000 | Retry delay |
| ApiKey | string? | null | Optional API key |
CRUDClientOptions (Async)
| Property | Type | Default | Description |
|---|---|---|---|
| BaseUrl | string | required | CRUD API base URL |
| TimeoutSeconds | int | 30 | HTTP timeout |
| ApiKey | string? | null | Optional API key |
Error Handling
using TAF.CRUD.SDK.Exceptions;
try
{
var result = await _client.Query("Orders").Insert(fields).WithContext(context).ExecuteAsync();
}
catch (CRUDClientException ex)
{
Console.WriteLine($"Failed: {ex.Message}");
Console.WriteLine($"Status: {ex.StatusCode}, Code: {ex.ErrorCode}");
}
When to Use Each Client
Use Synchronous Client When:
- Traditional request-response APIs
- Need immediate results
- Simple error handling
- Fast operations (< 2 seconds)
- No event consumers needed
Use Asynchronous Client When:
- Microservice-to-microservice communication
- Fire-and-forget behavior
- Event Carried State Transfer needed
- High-throughput scenarios
- RabbitMQ/MassTransit architecture
Best Practices
- Inject Concrete Types: Use
SyncCRUDClientorAsyncCRUDClientto choose specific client - Context is Required: Always provide tenant/app/environment/user context
- Retry Configuration: Adjust retry settings based on operation duration
- Event Metadata: Use
Context.Propertiesfor custom metadata in async operations - CorrelationId Tracking: Use returned GUID to correlate async operations with events
Troubleshooting
Connection Failures: Verify BaseUrl, network connectivity, API key validity
Event Not Received: Check MassTransit consumer registration, RabbitMQ connection, CorrelationId matching
Timeout Errors: Increase TimeoutSeconds (async client only waits for 202 Accepted)
Authentication Errors: Verify ApiKey and context headers (TenantId, AppId, etc.)
License
MIT License
Support
For issues or questions, contact the TAF platform team.
| Product | Versions 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 was computed. 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. |
-
net8.0
- Microsoft.Extensions.Http (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
- Newtonsoft.Json (>= 13.0.3)
- TAF.Infra.Contract (>= 1.11.5)
- TAF.Infra.MassTransit (>= 2.2.15)
- TAF.Infra.Redis (>= 1.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on TAF.CRUD.SDK:
| Package | Downloads |
|---|---|
|
TAF.Infra.Configuration
Cross-service configuration management. Reads config from TABD_Settings via CRUD SDK, caches via ICacheService, and invalidates instantly via SettingsChangedEvent through MassTransit. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 6.0.8 | 0 | 5/24/2026 |
| 6.0.7 | 38 | 5/23/2026 |
| 6.0.6 | 239 | 4/30/2026 |
| 6.0.5 | 199 | 4/22/2026 |
| 6.0.4 | 104 | 4/22/2026 |
| 6.0.3 | 135 | 3/19/2026 |
| 6.0.2 | 151 | 3/6/2026 |
| 6.0.1 | 178 | 2/10/2026 |
| 6.0.0 | 758 | 2/6/2026 |
| 5.2.1 | 124 | 1/28/2026 |
| 5.2.0 | 124 | 1/22/2026 |
| 5.1.5 | 124 | 1/21/2026 |
| 5.1.4 | 142 | 1/10/2026 |
| 5.1.3 | 155 | 1/2/2026 |
| 5.1.2 | 153 | 12/30/2025 |
| 5.1.1 | 215 | 12/22/2025 |
| 5.1.0 | 343 | 12/17/2025 |
| 5.0.0 | 475 | 12/9/2025 |
| 4.1.0 | 198 | 11/28/2025 |
| 4.0.0 | 217 | 11/27/2025 |