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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TAF.CRUD.SDK" Version="6.0.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TAF.CRUD.SDK" Version="6.0.8" />
                    
Directory.Packages.props
<PackageReference Include="TAF.CRUD.SDK" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TAF.CRUD.SDK --version 6.0.8
                    
#r "nuget: TAF.CRUD.SDK, 6.0.8"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TAF.CRUD.SDK@6.0.8
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TAF.CRUD.SDK&version=6.0.8
                    
Install as a Cake Addin
#tool nuget:?package=TAF.CRUD.SDK&version=6.0.8
                    
Install as a Cake Tool

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):

  • TenantId from context.TenantId
  • AppId from context.AppId
  • EnvironmentId from context.EnvironmentId
  • UserId from context.UserId
  • CorrelationId from context.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

  1. Inject Concrete Types: Use SyncCRUDClient or AsyncCRUDClient to choose specific client
  2. Context is Required: Always provide tenant/app/environment/user context
  3. Retry Configuration: Adjust retry settings based on operation duration
  4. Event Metadata: Use Context.Properties for custom metadata in async operations
  5. 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed