using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Model; using System.Collections.ObjectModel; namespace Data.EF.Players { public sealed class PlayerDbManager : IManager { private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private readonly DiceAppDbContext db; public PlayerDbManager(DiceAppDbContext db) { if (db is null) { ArgumentNullException ex = new(nameof(db), "param should not be null"); logger.Error(ex, "attempted to construct PlayerDbManager with a null context"); throw ex; } this.db = db; } /// /// side effect: entity's name is trimmed. /// /// /// /// private static void CleanPlayerEntity(PlayerEntity entity) { if (entity is null) { ArgumentNullException ex = new(nameof(entity), "param should not be null"); logger.Warn(ex); throw ex; } if (string.IsNullOrWhiteSpace(entity.Name)) { ArgumentException ex = new("Name property should not be null or whitespace", nameof(entity)); logger.Warn(ex); throw ex; } entity.Name = entity.Name.Trim(); } /// /// adds a non-null PlayerEntity with a valid name to this mgr's context /// /// the entity to add /// /// /// public Task Add(PlayerEntity toAdd) { CleanPlayerEntity(toAdd); if (db.PlayerEntity.Where(entity => entity.Name == toAdd.Name).Any()) { ArgumentException ex = new("this username is already taken", nameof(toAdd)); logger.Warn(ex); throw ex; } return InternalAdd(toAdd); } private async Task InternalAdd(PlayerEntity toAdd) { EntityEntry ee = await db.PlayerEntity.AddAsync(toAdd); await db.SaveChangesAsync(); logger.Info("Added {0}", ee.Entity.ToString()); return (PlayerEntity)ee.Entity; } public async Task> GetAll() { List players = new(); await Task.Run(() => players.AddRange(db.PlayerEntity)); return new ReadOnlyCollection(players); } /// /// This will throw an exception if no player with such name exists. /// If you want to know whether any player with that name exists, call IsPresentByName() /// /// /// /// /// public Task GetOneByName(string name) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Name property should not be null or whitespace", nameof(name)); } name = name.Trim(); return InternalGetOneByName(name); } private async Task InternalGetOneByName(string name) { return await db.PlayerEntity.Where(p => p.Name == name).FirstAsync(); } public async Task IsPresentByName(string name) { if (string.IsNullOrWhiteSpace(name)) { return false; } name = name.Trim(); return await db.PlayerEntity.Where(p => p.Name == name).FirstOrDefaultAsync() is not null; } /// /// removes a non-null PlayerEntity with a valid name from this mgr's context /// /// the entity to remove /// /// public void Remove(PlayerEntity toRemove) { CleanPlayerEntity(toRemove); bool isPresent = IsPresentByID(toRemove.ID).Result; if (isPresent) { db.PlayerEntity.Remove(toRemove); logger.Info("Removed {0}", toRemove.ToString()); db.SaveChanges(); } } /// /// updates a non-null PlayerEntity with a valid name in this mgr's context. This cannot update an ID /// /// the entity to update /// the entity to replace 'before' /// the updated entity /// /// public Task Update(PlayerEntity before, PlayerEntity after) { PlayerEntity[] args = { before, after }; foreach (PlayerEntity entity in args) { CleanPlayerEntity(entity); } if (before.ID != after.ID) { ArgumentException ex = new("ID cannot be updated", nameof(after)); logger.Warn(ex); throw ex; } return InternalUpdate(before, after); } private async Task InternalUpdate(PlayerEntity before, PlayerEntity after) { Remove(before); return await Add(after); } /// /// This will throw an exception if no player with such ID exists. /// If you want to know whether any player with that ID exists, call IsPresentByID() /// /// the ID to look for /// PlayerEntity with that ID /// public async Task GetOneByID(Guid ID) { return await db.PlayerEntity.FirstAsync(p => p.ID == ID); } public async Task IsPresentByID(Guid ID) { return await db.PlayerEntity.FirstOrDefaultAsync(p => p.ID == ID) is not null; } } }