Getting Started with Rapp
Welcome to Rapp! This guide will walk you through setting up Rapp to achieve safe, high-performance binary caching in your .NET applications.
Rapp uses Source Generators to create AOT-compatible serialization code that is both faster than JSON and safer than raw binary serializers (like MemoryPack) for distributed systems.
Prerequisites
- .NET 10.0 or later
- An existing ASP.NET Core or Console application
Step 1: Installation
Add the Rapp package to your project. This package includes both the runtime library and the source generators.
dotnet add package RappYou will also need Microsoft.Extensions.Caching.Hybrid if you haven't added it yet:
dotnet add package Microsoft.Extensions.Caching.HybridStep 2: Define Your Data Models
Rapp works by inspecting your data models at compile-time. To make a class compatible:
- Make the class
partial. - Add the
[RappCache]attribute. - Add the
[MemoryPackable]attribute (Rapp builds on MemoryPack's engine).
using Rapp;
using MemoryPack;
namespace MyApp.Models;
// 1. partial class is required
// 2. [RappCache] adds schema safety & HybridCache integration
// 3. [MemoryPackable] adds high-performance binary serialization
[RappCache]
[MemoryPackable]
public partial class UserProfile
{
public Guid Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
public DateTime LastLogin { get; set; }
}Why
partial? Rapp generates aUseRappForUserProfile()extension method and a robust serializer implementation in a separate file during compilation.
Step 3: Register Services
In your Program.cs (or startup code), register HybridCache and chain the generated UseRappFor{Type} methods.
var builder = WebApplication.CreateBuilder(args);
// Register HybridCache and strict-type Rapp serializers
builder.Services.AddHybridCache(options =>
{
options.MaximumPayloadBytes = 1024 * 1024; // 1MB
})
// Auto-generated extension methods for your types
.UseRappForUserProfile();Step 4: Use HybridCache
Inject HybridCache into your services or endpoints. You don't need to change how you use the cache—Rapp works transparently behind the scenes.
app.MapGet("/users/{id}", async (Guid id, HybridCache cache) =>
{
// Rapp handles the binary serialization & safety checks automatically
var user = await cache.GetOrCreateAsync(
key: $"user:{id}",
factory: async ct => await dbContext.Users.FindAsync(id, ct)
);
return user;
});Step 5: Handling Schema Changes (The "Magic" Part)
One of Rapp's main features is Schema Evolution Safety. When you modify your class, Rapp automatically protects your application from crashing due to old/incompatible binary data in the cache.
Scenario: Adding a Field
Let's say you update UserProfile to add a PhoneNumber:
[RappCache]
[MemoryPackable]
public partial class UserProfile
{
// ... existing fields ...
public string? PhoneNumber { get; set; } // New field
}What happens?
- Rapp detects the schema change at compile-time.
- It generates a new unique Schema Hash for version 2.
- When your app reads an old (v1) cache entry, the hash mismatch is detected.
- Rapp treats this as a Cache Miss (instead of crashing).
- The
factorymethod runs, fetches fresh data from the DB, and writes the new (v2) format to the cache.
You don't need to do manual versioning! Just modify your class, deploy, and Rapp handles the rest.
Step 6: Native AOT Publishing
Rapp is 100% Native AOT compatible. To publish your app as a native executable:
Enable AOT in your
.csproj:xml<PropertyGroup> <PublishAot>true</PublishAot> </PropertyGroup>Publish:
bashdotnet publish -c Release
Because Rapp uses Source Generators instead of Reflection, your app remains trim-safe and highly optimized.
Troubleshooting
- "Method UseRappForX not found": Ensure your class is
public(orinternal) andpartialand has the[RappCache]attribute. Try rebuilding the project to trigger source generation. - "Type is not serializable": Ensure all nested types in your model are also marked with
[MemoryPackable].
Ready for more? Check out the README for advanced topics like Telemetry and Custom Validation.