This guide helps you migrate from existing caching solutions to Rapp.
- From JSON (System.Text.Json)
- From MemoryPack
- From Other Binary Serializers
- Handling Schema Evolution
public class UserData
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
}
// Configure HybridCache with JSON
builder.Services.AddHybridCache(options =>
{
// JSON is the default
});
// Usage
var cached = await cache.GetOrCreateAsync(
"user-123",
async ct => await GetUserDataAsync(123, ct)
);using Rapp;
using MemoryPack;
[RappCache] // Add this attribute
[MemoryPackable] // Add this attribute
public partial class UserData // Add 'partial' keyword
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
}
// Configure HybridCache with Rapp
builder.Services.AddHybridCache(options =>
{
// Options configuration
}).UseRappForUserData(); // Generated extension method
// Usage (same as before)
var cached = await cache.GetOrCreateAsync(
"user-123",
async ct => await GetUserDataAsync(123, ct)
);- 5.6× faster serialization, 17.9× faster deserialization
- 65% less memory usage
- Smaller cache payloads (70% smaller than JSON)
- Schema validation prevents crashes on deployments
[MemoryPackable]
public partial class CacheItem
{
public int Id { get; set; }
public string Value { get; set; }
}
// Custom serializer
public class MemoryPackHybridSerializer<T> : IHybridCacheSerializer<T>
{
public void Serialize(T value, IBufferWriter<byte> target)
{
MemoryPackSerializer.Serialize(target, value);
}
public T Deserialize(ReadOnlySequence<byte> source)
{
return MemoryPackSerializer.Deserialize<T>(source);
}
}
// Manual registration
builder.Services.AddHybridCache()
.AddSerializer<CacheItem, MemoryPackHybridSerializer<CacheItem>>();[RappCache] // Add this attribute
[MemoryPackable]
public partial class CacheItem
{
public int Id { get; set; }
public string Value { get; set; }
}
// Automatic registration
builder.Services.AddHybridCache()
.UseRappForCacheItem(); // Generated extension method- No custom serializer code - fully generated
- Schema validation prevents crashes on property changes
- Same performance as raw MemoryPack
- Safer deployments with automatic version checking
MemoryPack - Crashes on schema mismatch:
// Old version
[MemoryPackable]
public partial class Item
{
public int Id { get; set; }
}
// New version - CRASHES on old cached data!
[MemoryPackable]
public partial class Item
{
public int Id { get; set; }
public string Name { get; set; } // Added property
}Rapp - Handles schema changes gracefully:
// Old version
[RappCache]
[MemoryPackable]
public partial class Item
{
public int Id { get; set; }
}
// New version - Returns default, cache rebuilds automatically
[RappCache]
[MemoryPackable]
public partial class Item
{
public int Id { get; set; }
public string Name { get; set; } // Schema mismatch detected, returns default
}// Before
[ProtoContract]
public class MyData
{
[ProtoMember(1)]
public int Id { get; set; }
}
// After
[RappCache]
[MemoryPackable]
public partial class MyData
{
public int Id { get; set; }
}// Before
[MessagePackObject]
public class MyData
{
[Key(0)]
public int Id { get; set; }
}
// After
[RappCache]
[MemoryPackable]
public partial class MyData
{
public int Id { get; set; }
}-
Add New Properties with Defaults
[RappCache] [MemoryPackable] public partial class User { public int Id { get; set; } public string Name { get; set; } public string? Email { get; set; } = null; // Safe to add }
-
Use Nullable Types for Optional Data
[RappCache] [MemoryPackable] public partial class Order { public int Id { get; set; } public DateTime? ProcessedAt { get; set; } // Nullable }
-
Clear Cache on Breaking Changes
// Option 1: Clear specific cache entries await cache.RemoveAsync("user-*"); // Option 2: Use versioned keys var key = $"v2:user-{userId}"; // Version prefix
Rapp automatically detects schema changes and returns default(T):
// Old cached data exists
var result = await cache.GetOrCreateAsync(
"data-key",
async ct => await FetchDataAsync(ct)
);
// If schema changed:
// - Old data is rejected (returns default)
// - Factory function is called
// - New data with new schema is cached// Enable telemetry to track schema mismatches
RappConfiguration.EnableTelemetry = true;
// Metrics available:
// - rapp_cache_hits_total
// - rapp_cache_misses_total
// - rapp_schema_mismatches_total (when enabled)// Disable telemetry
RappConfiguration.EnableTelemetry = false;
// Enable detailed errors (development only)
RappConfiguration.EnableDetailedErrors = true;
// Throw exceptions on schema mismatch (testing only)
RappConfiguration.ThrowOnSchemaMismatch = true;Generated extension methods provide type-safe registration:
builder.Services.AddHybridCache()
.UseRappForUser() // For User class
.UseRappForProduct() // For Product class
.UseRappForOrder(); // For Order class| Scenario | JSON | MemoryPack | Rapp | Notes |
|---|---|---|---|---|
| Serialize | 1,764.1 ns | 197.0 ns | 397.2 ns | Rapp overhead: ~102% |
| Deserialize | 4,238.1 ns | 180.0 ns | 240.9 ns | Rapp overhead: ~34% |
| vs JSON | 1.0× | 9.0× faster (ser) | 4.4× faster (ser) | Rapp: 17.6× faster (deser) |
| HybridCache | N/A | 416.5 ns | 436.9 ns | Rapp overhead: ~4.9% |
| Realistic (100 ops) | N/A | 44.1 μs | 30.5 μs | Rapp 31% faster! |
| Schema Safety | Rapp matches JSON | |||
| AOT Compatible | Rapp matches MemoryPack |
Benchmarks: .NET 10.0.1, Intel Core i7-4980HQ @ 2.80GHz, macOS. See benchmarks/ for details.
Solution: Add both attributes:
[RappCache]
[MemoryPackable]
public partial class MyClass { }Cause: Missing partial keyword
Solution:
// Wrong
[RappCache]
public class MyClass { }
// Correct
[RappCache]
public partial class MyClass { }Cause: Schema changed, old cache data rejected
Solution: This is expected behavior. Cache will rebuild with new schema.
- Always use
partialclasses - Add properties as nullable when possible
- Use versioned cache keys for breaking changes
- Monitor schema mismatch metrics
- Test deployments with schema changes
- Keep telemetry enabled in production
- Check the Documentation
- View Examples
- Open an Issue
- Read the FAQ