# Realm .NET SDK Realm .NET is a mobile-first database SDK that runs directly on devices (iOS, Android, Windows, macOS, Linux) enabling local data persistence and optional synchronization with MongoDB Atlas. It provides a modern object-oriented API where database objects are directly exposed as C# classes with full LINQ query support, eliminating the need for traditional ORMs. The SDK handles data persistence through native bindings to Realm Core, offering performance that exceeds SQLite while maintaining rich features like relationships, transactions, and reactive change notifications. The SDK architecture consists of three primary layers: the managed C# API (Realm namespace), the Fody weaver for code generation, and native wrappers to Realm Core. Objects inherit from `IRealmObject` (source generator) or `RealmObject` (Fody weaver) to gain persistence capabilities. All database operations occur within implicit read transactions or explicit write transactions. The SDK supports multiple configuration types including local (`RealmConfiguration`), in-memory (`InMemoryConfiguration`), and synchronized realms (`PartitionSyncConfiguration`, `FlexibleSyncConfiguration`) for Atlas Device Sync integration. ## Core Database Operations ### Opening a Realm Instance ```csharp using Realms; // Open with default configuration var realm = Realm.GetInstance(); // Open with custom file path var config = new RealmConfiguration("/path/to/my.realm"); var realm = Realm.GetInstance(config); // Open with encryption (64-byte key) var encryptedConfig = new RealmConfiguration { EncryptionKey = new byte[64] { /* your 512-bit key */ } }; var encryptedRealm = Realm.GetInstance(encryptedConfig); // Open asynchronously (performs migrations on background thread) var realm = await Realm.GetInstanceAsync(config); // In-memory realm (data not persisted to disk) var inMemoryConfig = new InMemoryConfiguration("temp-realm"); var tempRealm = Realm.GetInstance(inMemoryConfig); ``` ### Defining Realm Objects ```csharp using Realms; using MongoDB.Bson; // Modern approach: IRealmObject (requires source generator) public partial class Dog : IRealmObject { [PrimaryKey] [MapTo("_id")] public ObjectId Id { get; set; } = ObjectId.GenerateNewId(); public string Name { get; set; } public int Age { get; set; } public string? Breed { get; set; } public Person? Owner { get; set; } [Indexed] public string Color { get; set; } [Backlink(nameof(Person.Dogs))] public IQueryable Owners { get; } // Collections - automatically initialized public IList Tags { get; } [Ignored] public string DisplayName => $"{Name} ({Age} years old)"; } // Legacy approach: RealmObject (requires Fody weaver) public class Cat : RealmObject { [PrimaryKey] public int Id { get; set; } [Required] public string Name { get; set; } public IList Kittens { get; } } // Embedded objects (lifecycle tied to parent) public partial class Address : IEmbeddedObject { public string Street { get; set; } public string City { get; set; } public string ZipCode { get; set; } } public partial class Person : IRealmObject { [PrimaryKey] public Guid Id { get; set; } = Guid.NewGuid(); public string FirstName { get; set; } public string LastName { get; set; } public Address? HomeAddress { get; set; } public IList Dogs { get; } } ``` ### Writing Data (Transactions) ```csharp // Method 1: Using Write() with action realm.Write(() => { var dog = new Dog { Name = "Rex", Age = 3, Breed = "Golden Retriever", Color = "Golden" }; realm.Add(dog); }); // Method 2: Using Write() with return value var createdDog = realm.Write(() => { return realm.Add(new Dog { Name = "Max", Age = 5 }); }); // Method 3: Manual transaction management using (var transaction = realm.BeginWrite()) { var person = new Person { FirstName = "John", LastName = "Smith" }; realm.Add(person); // Modify existing objects var existingDog = realm.All().First(); existingDog.Age = 4; transaction.Commit(); // If Commit() is not called, changes are rolled back automatically } // Method 4: Async write (non-blocking commit) await realm.WriteAsync(() => { realm.Add(new Dog { Name = "Buddy", Age = 2 }); }); // Updating existing objects with primary key realm.Write(() => { var dog = new Dog { Id = existingId, Name = "Updated Name" }; realm.Add(dog, update: true); // Updates existing object }); // Bulk operations var dogs = new List { new Dog { Name = "Dog1", Age = 1 }, new Dog { Name = "Dog2", Age = 2 } }; realm.Write(() => realm.Add(dogs)); // Error handling try { realm.Write(() => { realm.Add(new Dog { Name = "Test", Age = -1 }); }); } catch (RealmInvalidTransactionException ex) { Console.WriteLine($"Transaction error: {ex.Message}"); } ``` ### Querying Data ```csharp // Get all objects of a type var allDogs = realm.All(); // LINQ queries (lazy evaluation) var youngDogs = realm.All() .Where(d => d.Age < 3) .OrderBy(d => d.Name); // Filtering with complex expressions var specificDogs = realm.All() .Where(d => d.Age >= 2 && d.Age <= 5) .Where(d => d.Color == "Golden" || d.Breed == "Labrador"); // String operations var filteredByName = realm.All() .Where(d => d.Name.StartsWith("R")) .Where(d => d.Breed.Contains("Retriever")); // Full-text search (requires FTS index) var searchResults = realm.All() .Where(d => QueryMethods.FullTextSearch(d.Breed, "golden retriever")); // Case-insensitive search var caseInsensitive = realm.All() .Where(d => QueryMethods.Contains(d.Name, "rex", StringComparison.OrdinalIgnoreCase)); // Wildcard pattern matching var patternMatch = realm.All() .Where(d => QueryMethods.Like(d.Name, "R*x")); // Geospatial queries var nearbyLocations = realm.All() .Where(p => QueryMethods.GeoWithin( p.Location, new GeoCircle((40.7128, -74.0060), 5000) )); // Accessing relationships var dogsWithOwners = realm.All() .Where(d => d.Owner != null) .Select(d => new { d.Name, OwnerName = d.Owner.FirstName }); // Querying through relationships var personsWithOldDogs = realm.All() .Where(p => p.Dogs.Any(d => d.Age > 10)); // Aggregations var count = realm.All().Count(); var averageAge = realm.All().Average(d => d.Age); var oldestDog = realm.All().Max(d => d.Age); // Sorting var sortedDogs = realm.All() .OrderBy(d => d.Age) .ThenBy(d => d.Name); // Pagination var page = realm.All() .Skip(20) .Take(10); // Materialization (converts to List) var dogsList = realm.All().ToList(); // First/Single operations var firstDog = realm.All().First(); var firstOrDefault = realm.All().FirstOrDefault(); var singleDog = realm.All().Single(d => d.Id == targetId); ``` ### Finding Objects by Primary Key ```csharp // Fast lookup using primary key var dog = realm.Find(existingId); if (dog != null) { Console.WriteLine($"Found: {dog.Name}"); } // Works with different primary key types var personByGuid = realm.Find(guidId); var dogByObjectId = realm.Find(objectId); var catById = realm.Find(123); var itemByString = realm.Find("key-123"); // Returns null if not found var notFound = realm.Find(nonExistentId); if (notFound == null) { Console.WriteLine("Object doesn't exist"); } ``` ### Deleting Data ```csharp // Delete single object realm.Write(() => { var dog = realm.Find(dogId); if (dog != null) { realm.Remove(dog); } }); // Delete multiple objects realm.Write(() => { var oldDogs = realm.All().Where(d => d.Age > 15); realm.RemoveRange(oldDogs); }); // Delete all objects of a type realm.Write(() => { realm.RemoveAll(); }); // Delete all data in realm realm.Write(() => { realm.RemoveAll(); }); ``` ### Collections and Relationships ```csharp // Working with lists realm.Write(() => { var person = new Person { FirstName = "Alice" }; realm.Add(person); person.Dogs.Add(new Dog { Name = "Spot", Age = 3 }); person.Dogs.Add(new Dog { Name = "Buddy", Age = 5 }); // Access by index var firstDog = person.Dogs[0]; // Remove from list person.Dogs.RemoveAt(1); // Clear entire list person.Dogs.Clear(); }); // Sets (no duplicates, unordered) public partial class Tag : IRealmObject { public ISet Labels { get; } } realm.Write(() => { var tag = new Tag(); tag.Labels.Add("important"); tag.Labels.Add("urgent"); tag.Labels.Add("important"); // Silently ignored (duplicate) realm.Add(tag); }); // Dictionaries (key-value pairs) public partial class Config : IRealmObject { public IDictionary Settings { get; } } realm.Write(() => { var config = new Config(); config.Settings["theme"] = "dark"; config.Settings["language"] = "en"; realm.Add(config); }); // Accessing dictionary var theme = config.Settings["theme"]; if (config.Settings.ContainsKey("timeout")) { var timeout = config.Settings["timeout"]; } // Inverse relationships (backlinks) var person = realm.All().First(); var theirDogs = person.Dogs; // Forward relationship foreach (var dog in realm.All()) { var owners = dog.Owners; // Inverse relationship via [Backlink] } ``` ## Schema Management and Migrations ### Schema Versioning and Migration ```csharp // Basic migration example var config = new RealmConfiguration { SchemaVersion = 2, MigrationCallback = (migration, oldSchemaVersion) => { if (oldSchemaVersion < 1) { // Rename property migration.RenameProperty("Dog", "DogName", "Name"); } if (oldSchemaVersion < 2) { // Migrate data from old to new schema var oldDogs = migration.OldRealm.DynamicApi.All("Dog"); var newDogs = migration.NewRealm.All(); foreach (var oldDog in oldDogs) { var newDog = migration.FindInNewRealm(oldDog); if (newDog != null) { // Compute new field from old data var firstName = oldDog.DynamicApi.Get("FirstName"); var lastName = oldDog.DynamicApi.Get("LastName"); newDog.FullName = $"{firstName} {lastName}"; } } } } }; var realm = Realm.GetInstance(config); // Remove obsolete types var config = new RealmConfiguration { SchemaVersion = 3, MigrationCallback = (migration, oldSchemaVersion) => { if (oldSchemaVersion < 3) { // Remove type and all its data migration.RemoveType("ObsoleteModel"); } } }; // Schema validation during development var devConfig = new RealmConfiguration { ShouldDeleteIfMigrationNeeded = true // WARNING: Deletes database! }; // Readonly realm (no migration needed) var readonlyConfig = new RealmConfiguration { IsReadOnly = true, SchemaVersion = 1 }; // Compacting realm to reclaim space bool compacted = Realm.Compact(config); if (compacted) { Console.WriteLine("Realm file was compacted"); } ``` ## Reactive Programming and Change Notifications ### Observing Collection Changes ```csharp // Subscribe to collection changes var dogs = realm.All().Where(d => d.Age > 3); var token = dogs.SubscribeForNotifications((sender, changes) => { if (changes == null) { // Initial callback with current data Console.WriteLine($"Initial count: {sender.Count}"); return; } // Process deletions foreach (var index in changes.DeletedIndices) { Console.WriteLine($"Deleted at index: {index}"); } // Process insertions foreach (var index in changes.InsertedIndices) { var insertedDog = sender[index]; Console.WriteLine($"Inserted: {insertedDog.Name}"); } // Process modifications foreach (var index in changes.ModifiedIndices) { var modifiedDog = sender[index]; Console.WriteLine($"Modified: {modifiedDog.Name}"); } // Process moves (for sorted results) foreach (var move in changes.Moves) { Console.WriteLine($"Moved from {move.From} to {move.To}"); } }); // Unsubscribe when done token.Dispose(); // Object-level notifications var dog = realm.Find(dogId); var objectToken = dog.SubscribeForNotifications((sender, changes) => { if (changes == null) { return; // Initial callback } if (changes.IsDeleted) { Console.WriteLine("Dog was deleted"); return; } // Check which properties changed foreach (var propertyName in changes.ChangedProperties) { Console.WriteLine($"Property changed: {propertyName}"); } }); // Realm-level change notifications realm.RealmChanged += (sender, eventArgs) => { Console.WriteLine("Realm changed (transaction committed)"); // Refresh UI or process changes }; ``` ## Atlas Device Sync Integration ### App Configuration and User Authentication ```csharp using Realms.Sync; // Initialize App instance var appConfig = new AppConfiguration("your-app-id") { BaseUri = new Uri("https://realm.mongodb.com"), MetadataPersistenceMode = MetadataPersistenceMode.Encrypted }; var app = App.Create(appConfig); // Anonymous authentication var anonymousUser = await app.LogInAsync(Credentials.Anonymous()); // Email/Password authentication await app.EmailPasswordAuth.RegisterUserAsync("user@example.com", "securePassword123"); var emailUser = await app.LogInAsync( Credentials.EmailPassword("user@example.com", "securePassword123") ); // API Key authentication var apiKey = await emailUser.ApiKeys.CreateAsync("my-api-key"); var apiKeyUser = await app.LogInAsync(Credentials.ApiKey(apiKey.Value)); // JWT authentication var jwtUser = await app.LogInAsync(Credentials.JWT("jwt-token-string")); // Google OAuth var googleUser = await app.LogInAsync( Credentials.Google("google-auth-code", GoogleCredentialType.AuthCode) ); // Facebook OAuth var fbUser = await app.LogInAsync(Credentials.Facebook("facebook-access-token")); // Apple authentication var appleUser = await app.LogInAsync(Credentials.Apple("apple-id-token")); // Custom function authentication var customUser = await app.LogInAsync( Credentials.Function(new { username = "testuser", secret = "secret123" }) ); // Check current user if (app.CurrentUser != null) { Console.WriteLine($"Logged in as: {app.CurrentUser.Id}"); // Log out await app.CurrentUser.LogOutAsync(); } // Manage API keys var allKeys = await emailUser.ApiKeys.FetchAllAsync(); await emailUser.ApiKeys.EnableAsync(apiKey.Id); await emailUser.ApiKeys.DisableAsync(apiKey.Id); await emailUser.ApiKeys.DeleteAsync(apiKey.Id); ``` ### Synchronized Realm Configuration ```csharp // Partition-Based Sync var partitionConfig = new PartitionSyncConfiguration("user-123", app.CurrentUser) { Schema = new[] { typeof(Dog), typeof(Person) } }; var syncedRealm = await Realm.GetInstanceAsync(partitionConfig); // Flexible Sync with subscriptions var flexConfig = new FlexibleSyncConfiguration(app.CurrentUser); var flexRealm = await Realm.GetInstanceAsync(flexConfig); // Define subscriptions for flexible sync await flexRealm.Subscriptions.UpdateAsync(() => { var myDogs = flexRealm.All().Where(d => d.Owner.Id == app.CurrentUser.Id); flexRealm.Subscriptions.Add(myDogs, new SubscriptionOptions { Name = "my-dogs" }); // Add multiple subscriptions var youngDogs = flexRealm.All().Where(d => d.Age < 5); flexRealm.Subscriptions.Add(youngDogs, new SubscriptionOptions { Name = "young-dogs" }); }); // Wait for subscriptions to synchronize await flexRealm.Subscriptions.WaitForSynchronizationAsync(); // Remove subscriptions await flexRealm.Subscriptions.UpdateAsync(() => { flexRealm.Subscriptions.Remove("my-dogs"); // Or remove all flexRealm.Subscriptions.RemoveAll(); }); // Sync session management var session = syncedRealm.SyncSession; // Monitor sync progress var downloadToken = session.GetProgressObservable( ProgressDirection.Download, ProgressMode.ReportIndefinitely ).Subscribe(progress => { Console.WriteLine($"Downloaded: {progress.TransferredBytes}/{progress.TransferableBytes}"); }); var uploadToken = session.GetProgressObservable( ProgressDirection.Upload, ProgressMode.ForCurrentlyOutstandingWork ).Subscribe(progress => { Console.WriteLine($"Uploaded: {progress.TransferredBytes}/{progress.TransferableBytes}"); }); // Wait for uploads/downloads await session.WaitForUploadAsync(); await session.WaitForDownloadAsync(); // Pause/resume sync session.Stop(); session.Start(); // Handle sync errors Session.Error += (sender, errorArgs) => { var error = errorArgs.Exception; Console.WriteLine($"Sync error: {error.Message}"); if (error is ClientResetException clientReset) { // Handle client reset clientReset.InitiateClientReset(); } }; ``` ### MongoDB Atlas Data Access ```csharp // Get MongoDB client var mongoClient = app.CurrentUser.GetMongoClient("mongodb-atlas"); var database = mongoClient.GetDatabase("myapp"); var collection = database.GetCollection("dogs"); // Insert document var newDog = new Dog { Name = "Remote", Age = 4 }; var insertResult = await collection.InsertOneAsync(newDog); Console.WriteLine($"Inserted ID: {insertResult.InsertedId}"); // Find documents var filter = new { Age = new { $gte = 3 } }; var remoteDogs = await collection.FindAsync(filter); foreach (var dog in remoteDogs) { Console.WriteLine($"{dog.Name}: {dog.Age} years old"); } // Find one document var oneDog = await collection.FindOneAsync(new { Name = "Remote" }); // Update document var updateResult = await collection.UpdateOneAsync( filter: new { Name = "Remote" }, update: new { Age = 5 } ); Console.WriteLine($"Modified count: {updateResult.ModifiedCount}"); // Delete document var deleteResult = await collection.DeleteOneAsync(new { Name = "Remote" }); Console.WriteLine($"Deleted count: {deleteResult.DeletedCount}"); // Aggregation pipeline var pipeline = new[] { new { $match = new { Age = new { $gte = 3 } } }, new { $group = new { _id = "$Breed", avgAge = new { $avg = "$Age" } } } }; var aggregateResults = await collection.AggregateAsync(pipeline); // Count documents var count = await collection.CountAsync(new { Breed = "Golden Retriever" }); // Call Atlas Function var result = await app.CurrentUser.Functions.CallAsync( "calculateTotal", 1, 2, 3, 4, 5 ); Console.WriteLine($"Function result: {result}"); // Call function with BsonDocument var bsonResult = await app.CurrentUser.Functions.CallAsync( "processData", new { userId = app.CurrentUser.Id, action = "update" } ); ``` ## Advanced Features ### Frozen Realms and Thread Safety ```csharp // Freeze realm for safe cross-thread access var frozenRealm = realm.Freeze(); // Frozen realm is immutable and thread-safe Task.Run(() => { var dogs = frozenRealm.All(); foreach (var dog in dogs) { Console.WriteLine(dog.Name); // Safe to access } }); // Freeze individual objects var dog = realm.Find(dogId); var frozenDog = dog.Freeze(); // Freeze query results var youngDogs = realm.All().Where(d => d.Age < 5); var frozenResults = youngDogs.Freeze(); // Check if frozen if (frozenRealm.IsFrozen) { // Cannot write to frozen realm // frozenRealm.Write(() => { }); // Throws exception } // Get live version from frozen var liveRealm = frozenRealm.Unfreeze(); ``` ### Encryption and Security ```csharp // Generate secure encryption key (64 bytes / 512 bits) var encryptionKey = new byte[64]; using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create()) { rng.GetBytes(encryptionKey); } // Store key securely (example using platform keychain) // iOS: Use Keychain, Android: Use KeyStore, etc. SaveKeySecurely(encryptionKey); // Open encrypted realm var config = new RealmConfiguration { EncryptionKey = LoadKeySecurely() }; var encryptedRealm = Realm.GetInstance(config); // All data is transparently encrypted/decrypted encryptedRealm.Write(() => { encryptedRealm.Add(new Dog { Name = "Secret", Age = 5 }); }); // Attempting to open with wrong key throws exception try { var wrongKeyConfig = new RealmConfiguration { DatabasePath = config.DatabasePath, EncryptionKey = new byte[64] // Wrong key }; var wrongRealm = Realm.GetInstance(wrongKeyConfig); } catch (RealmFileAccessErrorException ex) { Console.WriteLine("Wrong encryption key"); } ``` ### Performance Optimization ```csharp // RealmInteger for thread-safe counter operations public partial class Counter : IRealmObject { public RealmInteger Count { get; set; } } // Increment counter (atomic operation) realm.Write(() => { var counter = realm.All().First(); counter.Count.Increment(5); // Thread-safe increment }); // Batch operations for better performance realm.Write(() => { var dogs = new List(); for (int i = 0; i < 1000; i++) { dogs.Add(new Dog { Name = $"Dog{i}", Age = i % 15 }); } realm.Add(dogs); // Single transaction for all }); // Lazy evaluation with LINQ var query = realm.All() .Where(d => d.Age > 5) .OrderBy(d => d.Name); // No work done yet, query not executed var firstTen = query.Take(10).ToList(); // Now query executes, only fetches what's needed // Indexing for faster queries public partial class SearchableItem : IRealmObject { [Indexed(IndexType.General)] public string SearchField { get; set; } [Indexed(IndexType.FullText)] public string Description { get; set; } } // Refresh realm to get latest data if (realm.Refresh()) { Console.WriteLine("Realm updated with new data"); } // Async refresh (non-blocking) await realm.RefreshAsync(); ``` ### Dynamic Realm API ```csharp // Access realm dynamically without type information var dynamicRealm = realm.DynamicApi; // Query by string type name var allDogs = dynamicRealm.All("Dog"); foreach (var dog in allDogs) { var name = dog.DynamicApi.Get("Name"); var age = dog.DynamicApi.Get("Age"); Console.WriteLine($"{name}: {age}"); } // Create object dynamically realm.Write(() => { var dynamicDog = dynamicRealm.CreateObject("Dog", ObjectId.GenerateNewId()); dynamicDog.DynamicApi.Set("Name", "Dynamic Dog"); dynamicDog.DynamicApi.Set("Age", 7); }); // Useful for migrations and schema introspection var schema = realm.Schema; foreach (var objSchema in schema) { Console.WriteLine($"Type: {objSchema.Name}"); foreach (var property in objSchema) { Console.WriteLine($" {property.Name}: {property.Type}"); } } ``` ## Summary and Integration Patterns Realm .NET SDK provides a comprehensive local-first database solution with optional cloud synchronization capabilities. The primary use cases include offline-first mobile applications, real-time collaborative apps, IoT data persistence, and local caching layers for distributed systems. The SDK excels in scenarios requiring reactive UI updates through change notifications, complex object graphs with relationships, and applications needing to maintain data consistency across unreliable network connections. Performance is optimized for mobile constraints with lazy query evaluation, zero-copy architecture, and efficient memory management through automatic reference counting of live objects. Integration patterns typically involve defining model classes inheriting from `IRealmObject`, configuring realm instances with appropriate settings for local or synchronized scenarios, performing CRUD operations within write transactions, and subscribing to change notifications for reactive UI updates. For Atlas Device Sync scenarios, the pattern extends to user authentication through the App instance, configuring flexible sync subscriptions to control data synchronization scope, and handling sync lifecycle events. The SDK integrates seamlessly with MVVM frameworks, supports dependency injection patterns for realm instance management, and provides hooks for custom conflict resolution in synchronized realms. Advanced scenarios leverage frozen realms for cross-thread data access, encryption for sensitive data protection, and the dynamic API for runtime schema manipulation during migrations.