PartiTables 1.0.3
dotnet add package PartiTables --version 1.0.3
NuGet\Install-Package PartiTables -Version 1.0.3
<PackageReference Include="PartiTables" Version="1.0.3" />
<PackageVersion Include="PartiTables" Version="1.0.3" />
<PackageReference Include="PartiTables" />
paket add PartiTables --version 1.0.3
#r "nuget: PartiTables, 1.0.3"
#:package PartiTables@1.0.3
#addin nuget:?package=PartiTables&version=1.0.3
#tool nuget:?package=PartiTables&version=1.0.3
PartiTables
Azure Table Storage made simple for .NET
๐ง Why PartiTables?
Azure Table Storage is 95% cheaper than other NoSQL solutions and blazing fast โ but painful to use. PartiTables fixes that by providing clean, Entity Framework-style data access patterns.
Save Money - $10/month instead of $240/month
Type-Safe - IntelliSense, compile-time checking
Auto-Retry - Built-in resilience with Polly
Batch Operations - Save multiple entities atomically
Less Code - One class replaces hundreds of lines
๐ฆ Installation
dotnet add package PartiTables
๐ Quick Start
Before vs After
Without PartiTables:
// The old way: manual TableEntity manipulation
var entity = new TableEntity("partition", "row-key");
entity["FirstName"] = "John";
entity["LastName"] = "Doe";
await tableClient.UpsertEntityAsync(entity);
// ... tedious manual parsing when reading
// ... complex batch operations
// ... manual retry logic
With PartiTables:
// Use them like Entity Framework
var patient = new Patient { PatientId = "patient-123" };
patient.Meta.Add(new PatientMeta { FirstName = "John", Email = "john@example.com" });
patient.Consents.Add(new Consent { Type = "DataSharing" });
await repo.SaveAsync(patient);
// โ
All related records saved in ONE batch operation
// โ
Automatic retry with Polly resilience
// โ
RowKeys auto-generated from patterns
// โ
Strong typing, IntelliSense, compile-time safety
๐ก Key Features
Define Your Model with Declarative Patterns
[TablePartition("Customers", "{CustomerId}")]
public class Customer
{
public string CustomerId { get; set; }
[RowKeyPrefix("")]
public List<Order> Orders { get; set; } = new();
[RowKeyPrefix("")]
public List<Address> Addresses { get; set; } = new();
}
[RowKeyPattern("{CustomerId}-order-{OrderId}")]
public class Order : RowEntity
{
public string OrderId { get; set; } = Guid.NewGuid().ToString("N")[..8];
public decimal Amount { get; set; }
public string Status { get; set; } = "Pending";
}
[RowKeyPattern("{CustomerId}-address-{AddressId}")]
public class Address : RowEntity
{
public string AddressId { get; set; } = Guid.NewGuid().ToString("N")[..8];
public string City { get; set; } = default!;
}
Benefits:
- โ Self-documenting - pattern visible at a glance
- โ 60% less code than manual implementation
- โ Automatic key generation from properties
- โ Type-safe and compile-time validated
CRUD Operations
// Create
var customer = new Customer { CustomerId = "cust-123" };
customer.Orders.Add(new Order { Amount = 99.99m, Status = "Pending" });
await repo.SaveAsync(customer);
// Read
var customer = await repo.FindAsync("cust-123");
var orders = await repo.QueryCollectionAsync("cust-123", c => c.Orders);
// Update
loaded.Orders[0].Status = "Shipped";
await repo.SaveAsync(loaded);
// Delete
await repo.DeleteAsync("cust-123");
Automatic Batch Transactions
var customer = new Customer { CustomerId = "cust-123" };
customer.Orders.Add(new Order { Amount = 99.99m });
customer.Orders.Add(new Order { Amount = 45.50m });
customer.Addresses.Add(new Address { City = "Seattle" });
await repo.SaveAsync(customer);
// โ
All entities saved in ONE atomic batch operation
// โ
Either all succeed or all fail (within partition)
// โ
Up to 100 operations per batch
// โ
Automatic grouping by partition key
Built-in Resilience with Polly
// Automatic retry on transient failures
await repo.SaveAsync(customer);
// โ
Retries on network errors
// โ
Exponential backoff
// โ
Circuit breaker protection
You don't need to:
- โ Write retry logic
- โ Handle transient failures
- โ Implement exponential backoff
- โ Track failed operations
Handles Big Data with Automatic Rollback
PartiTables automatically handles datasets larger than Azure's 100-item batch limit with automatic rollback if any batch fails:
// Save 10,000 records across 100 batches
var salesData = GenerateSalesData("store-001", 10_000);
await repo.SaveAsync(salesData);
// โ
Automatically split into 100-item batches
// โ
If ANY batch fails, ALL previous batches are rolled back
// โ
Your data stays consistent - it's all-or-nothing!
What happens:
- Automatic batching - Splits into 100-item batches
- Sequential submission - Submits batches one at a time
- Rollback on failure - If any batch fails, previous batches are automatically deleted
- Exception preserved - Original error is re-thrown after cleanup
Benefits:
- โ No partial data - Either all items save or none do
- โ Unlimited scale - Handle 10,000+ items automatically
- โ Zero configuration - Works out of the box
๐ Powerful Querying
Partition-Scoped Queries (Fast)
// Get all data for a partition
var allCustomerData = await repo.FindAsync("customer-123");
// Get specific collection (more efficient)
var orders = await repo.QueryCollectionAsync("customer-123", c => c.Orders);
// Prefix-based queries
var orders2024 = await client.QueryByPrefixAsync("customer-123", "order-2024-");
Collection Filtering
// Load and filter in memory (for small datasets)
var customer = await repo.FindAsync("customer-123");
var pendingOrders = customer.Orders.Where(o => o.Status == "Pending").ToList();
// For larger datasets, query specific collection first
var allOrders = await repo.QueryCollectionAsync("customer-123", c => c.Orders);
var shipped = allOrders.Where(o => o.Status == "Shipped").ToList();
Query Best Practices
- โ Query within a single partition for best performance
- โ Use prefix-based queries for time-series data
- โ Load only the collections you need
- โ Avoid cross-partition queries when possible
- โ Filter in memory for small result sets
๐๏ธ How It Works
PartiTables transforms your object graph into optimized Table Storage entities:
// Your code
var patient = new Patient { PatientId = "patient-123" };
patient.Meta.Add(new PatientMeta { FirstName = "John", LastName = "Doe" });
patient.Consents.Add(new Consent { Type = "DataSharing" });
patient.Devices.Add(new DeviceLink { DeviceId = "device-001" });
await repo.SaveAsync(patient);
What happens behind the scenes:
โ
Batch Transaction to Table Storage
PartitionKey: clinic-001
patient-123-meta (Auto-generated from pattern)
patient-123-consent-a7b8 (Auto-generated from pattern)
patient-123-device-001 (Auto-generated from pattern)
โ
All saved atomically in one batch
โ
Automatic retry on failure
โ
Optimistic concurrency handled
Single Partition (Fast Queries):
PartitionKey: customer-123
RowKeys:
customer-123-order-001
customer-123-order-002
customer-123-address-001
Multi-Tenant Isolation:
PartitionKey: tenant-789
RowKeys:
tenant-789-user-001
tenant-789-user-002
tenant-789-setting-001
๐ฏ Perfect For
๐ฑ Multi-tenant SaaS | ๐ Customer orders & history | ๐ฅ Healthcare records
๐ Audit logs | ๐ค User profiles | ๐ IoT device data
๐ Time-series metrics | ๐ Notification queues | ๐ฆ Inventory tracking
๐ซ Event ticketing | ๐ฌ Chat history | ๐ Session management
๐งช Try It Now
cd PartiSample
dotnet run
Choose from 6 interactive demos showing real-world scenarios.
๐ Documentation
- Sample Demos - Real-world examples
- Quick Start Guide - Get started
- Big Data Tests - 10,000+ item examples
โ๏ธ Requirements
- .NET 8.0+
- Azure Storage or Azurite (local development)
๐ License
MIT License โ ยฉ 2025 PartiTech
๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
| 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
- Azure.Data.Tables (>= 12.11.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.9)
- Polly (>= 8.6.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v1.0.3 - Updated RowKey generation in existing entities.