求Xamarin.Forms+Realm数据仓库、UI绑定示例及仓库模式实现资源
Hey Sebastien, totally get where you're coming from—Realm's .NET docs can feel a bit sparse when you're trying to implement patterns like the Repository pattern, especially with Xamarin.Forms. Let me walk you through a practical, clean example that balances flexibility (so swapping out Realm later is a breeze) and plays nice with Realm's unique features.
First, define a generic repository interface that abstracts all basic data operations. This is the key to keeping your code decoupled from Realm—later, you can just implement this interface with a different provider (like SQLite or EF Core) without touching your ViewModels.
using System.Collections.Generic; using System.Threading.Tasks; public interface IRepository<T> where T : class { Task<T> GetByIdAsync(string id); Task<IEnumerable<T>> GetAllAsync(); Task AddAsync(T entity); Task UpdateAsync(T entity); Task DeleteAsync(T entity); }
Now create a Realm-specific implementation of this interface. Since Realm objects inherit from RealmObject, we'll add that constraint to ensure we're only working with Realm entities. We'll handle Realm's write transactions and thread safety here too.
using Realms; using System.Collections.Generic; using System.Threading.Tasks; public class RealmRepository<T> : IRepository<T> where T : RealmObject { private readonly Realm _realm; public RealmRepository() { // Initialize Realm with default configuration (pass a custom one if needed) _realm = Realm.GetInstance(); } public async Task<T> GetByIdAsync(string id) { // Wrap in Task.Run to avoid blocking the UI thread return await Task.Run(() => _realm.Find<T>(id)); } public async Task<IEnumerable<T>> GetAllAsync() { // Freeze results to make them thread-safe for UI thread access return await Task.Run(() => _realm.All<T>().Freeze().ToList()); } public async Task AddAsync(T entity) { // Use Realm's write transaction for safe inserts/upserts await _realm.WriteAsync(realm => { realm.Add(entity, update: true); // update: true handles upserts }); } public async Task UpdateAsync(T entity) { // Realm tracks changes to managed objects automatically—just wrap in a write transaction await _realm.WriteAsync(_ => { }); // For detached objects, reattach with update: true: // _realm.Add(entity, update: true); } public async Task DeleteAsync(T entity) { await _realm.WriteAsync(realm => { realm.Remove(entity); }); } }
Now you can use this repository in your ViewModels without any direct dependency on Realm. This makes your ViewModels testable and easy to refactor later.
First, define a Realm entity (your model):
using Realms; public class TodoItem : RealmObject { [PrimaryKey] public string Id { get; set; } = Guid.NewGuid().ToString(); public string Title { get; set; } public bool IsCompleted { get; set; } }
Then, in your ViewModel:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; using Xamarin.Forms; public class TodoViewModel : INotifyPropertyChanged { private readonly IRepository<TodoItem> _todoRepository; private ObservableCollection<TodoItem> _todoItems; public ObservableCollection<TodoItem> TodoItems { get => _todoItems; set { _todoItems = value; OnPropertyChanged(); } } public ICommand LoadTodoItemsCommand { get; } public ICommand AddTodoCommand { get; } public TodoViewModel() { // Inject the repository here (use DI for better practice!) _todoRepository = new RealmRepository<TodoItem>(); LoadTodoItemsCommand = new Command(async () => await LoadTodoItems()); AddTodoCommand = new Command(async () => await AddNewTodo()); } private async Task LoadTodoItems() { var items = await _todoRepository.GetAllAsync(); TodoItems = new ObservableCollection<TodoItem>(items); } private async Task AddNewTodo() { var newTodo = new TodoItem { Title = "New Todo Item" }; await _todoRepository.AddAsync(newTodo); await LoadTodoItems(); // Refresh the list } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
- Dependency Injection: Instead of newing up
RealmRepository<T>directly in ViewModels, use Xamarin.Forms' built-in DI or a library like Autofac. RegisterIRepository<T>withRealmRepository<T>so you can swap implementations in one place. - Thread Safety: Realm objects are tied to the thread they were created on. Use
Freeze()on query results if you're passing them to the UI thread, or initialize Realm on the background thread for heavy operations. - Extend the Interface: Add Realm-specific query methods to your interface if you need advanced filtering. For example:
Then implement it inTask<IEnumerable<T>> GetByQueryAsync(Func<IQueryable<T>, IQueryable<T>> query);RealmRepository<T>:public async Task<IEnumerable<T>> GetByQueryAsync(Func<IQueryable<T>, IQueryable<T>> query) { return await Task.Run(() => query(_realm.All<T>()).Freeze().ToList()); }
This setup keeps your code clean, testable, and flexible—exactly what you're looking for!
内容的提问来源于stack exchange,提问作者Sebastien Delaherche

