using MongoDB.Bson; using MongoDB.Driver; namespace Htmx.ApiDemo.Data; /// /// Scoped service wrapping the AppUser MongoDB collection. /// All operations use MongoDB's native async API — no EF, no LINQ-to-SQL /// translation, no RelationalModel, fully NativeAOT safe. /// public sealed class MongoDbService { private readonly IMongoCollection _users; public MongoDbService(IMongoClient client, IConfiguration configuration) { var db = client.GetDatabase(configuration["MongoDbName"] ?? "HtmxAppDb"); _users = db.GetCollection("users"); } /// Ensures the unique index on NormalizedEmail exists (idempotent). public async Task EnsureIndexesAsync(CancellationToken ct = default) { var indexKeys = Builders.IndexKeys.Ascending(u => u.NormalizedEmail); var indexOptions = new CreateIndexOptions { Unique = true, Name = "uq_normalizedEmail" }; var model = new CreateIndexModel(indexKeys, indexOptions); await _users.Indexes.CreateOneAsync(model, cancellationToken: ct); } /// Returns true if a user with the given normalised email already exists. public async Task EmailExistsAsync(string normalizedEmail, CancellationToken ct = default) { var filter = Builders.Filter.Eq(u => u.NormalizedEmail, normalizedEmail); return await _users.Find(filter).AnyAsync(ct); } /// Returns the user matching the normalised email, or null. public async Task FindByNormalizedEmailAsync(string normalizedEmail, CancellationToken ct = default) { var filter = Builders.Filter.Eq(u => u.NormalizedEmail, normalizedEmail); return await _users.Find(filter).FirstOrDefaultAsync(ct); } /// Inserts a new user document. public Task InsertAsync(AppUser user, CancellationToken ct = default) => _users.InsertOneAsync(user, cancellationToken: ct); }