using LANCommander.SDK.Extensions; using LANCommander.SDK; using LANCommander.Server.Data.Models; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System.Linq.Expressions; using System.Threading; using AutoMapper.QueryableExtensions; using AutoMapper; using LANCommander.Server.Data.Enums; namespace LANCommander.Server.Data { public class Repository : IDisposable where T : class, IBaseModel { public readonly DatabaseContext Context; private readonly IMapper Mapper; private readonly IHttpContextAccessor HttpContextAccessor; private readonly ILogger Logger; private List, IQueryable>> Modifiers = new List, IQueryable>>(); private User User; public Repository( IDbContextFactory contextFactory, IMapper mapper, IHttpContextAccessor httpContextAccessor, ILogger logger) { Context = contextFactory.CreateDbContext(); Mapper = mapper; HttpContextAccessor = httpContextAccessor; Logger = logger; Logger?.LogDebug("Opened up context {ContextId}", Context.ContextId); } public Repository( IDbContextFactory contextFactory, IMapper mapper, IHttpContextAccessor httpContextAccessor) { Context = contextFactory.CreateDbContext(); Mapper = mapper; HttpContextAccessor = httpContextAccessor; } private DbSet DbSet { get { return Context.Set(); } } private DbSet UserDbSet { get { return Context.Set(); } } public Repository Query(Func, IQueryable> modifier) { Modifiers.Add(modifier); return this; } private IQueryable Query(Expression> predicate) { var queryable = DbSet.AsQueryable().Where(predicate); foreach (var modifier in Modifiers) { queryable = modifier.Invoke(queryable); } if (Modifiers.Any(m => m.Method.Name.StartsWith(""))) queryable = queryable.AsSplitQuery(); return queryable; } public IQueryable Query() { return DbSet.AsQueryable(); } public Repository AsNoTracking() { return Query((queryable) => { return queryable.AsNoTracking(); }); } public Repository Include(params Expression>[] expressions) { return Query((queryable) => { foreach (var expression in expressions) { queryable = queryable.Include(expression); } return queryable; }); } public Repository SortBy(Expression> expression, SortDirection direction = SortDirection.Ascending) { switch (direction) { case SortDirection.Descending: return Query((queryable) => { return queryable.OrderByDescending(expression); }); case SortDirection.Ascending: default: return Query((queryable) => { return queryable.OrderBy(expression); }); } } /// /// With the current query, get a paginated list of results. Optimizes the query by only getting the amount of records specified by page size. /// /// The query to filter results by /// The current page number (indexed by 1) /// The number of results to get per page /// public async Task> PaginateAsync(Expression> expression, int pageNumber, int pageSize) { try { var results = new PaginatedResults(); results.Count = await Query(expression).CountAsync(); results.Results = await Query(expression).Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync(); return results; } finally { Modifiers.Clear(); } } public async Task> GetAsync() { try { return await Query().ToListAsync(); } finally { Reset(); } } public async Task > GetAsync(Expression> predicate) { try { return await Query(predicate).ToListAsync(); } finally { Reset(); } } public async Task> GetAsync(Expression> predicate) { try { return await Query(predicate).ProjectTo(Mapper.ConfigurationProvider).ToListAsync(); } finally { Reset(); } } public async Task FirstAsync(Expression> predicate) { try { return await Query(predicate).FirstAsync(); } finally { Reset(); } } public async Task FirstAsync(Expression> predicate) { try { return await Query(predicate).ProjectTo(Mapper.ConfigurationProvider).FirstAsync(); } finally { Reset(); } } public async Task FirstAsync(Expression> predicate, Expression> orderKeySelector) { try { return await Query(predicate).OrderByDescending(orderKeySelector).FirstAsync(); } finally { Reset(); } } public async Task FirstAsync(Expression> predicate, Expression> orderKeySelector) { try { return await Query(predicate).ProjectTo(Mapper.ConfigurationProvider).OrderByDescending(orderKeySelector).FirstAsync(); } finally { Reset(); } } public async Task FindAsync(Guid id) { try { //using (var op = Logger.BeginOperation("Finding entity with ID {EntityId}", id)) //{ var entity = await Query(x => x.Id == id).FirstAsync(); /// op.Complete(); return entity; //} } finally { Reset(); } } public async Task FindAsync(Guid id) { try { //using (var op = Logger.BeginOperation("Finding entity with ID {EntityId}", id)) //{ var entity = await Query(x => x.Id == id).ProjectTo(Mapper.ConfigurationProvider).FirstAsync(); // op.Complete(); return entity; //} } finally { Reset(); } } public async Task FirstOrDefaultAsync(Expression> predicate) { try { //using (var op = Logger.BeginOperation("Getting first or default of type {EntityType}", typeof(T).Name)) //{ var entity = await Query(predicate).FirstOrDefaultAsync(); // op.Complete(); return entity; //} } finally { Reset(); } } public async Task FirstOrDefaultAsync(Expression> predicate) { try { //using (var op = Logger.BeginOperation("Getting first or default of type {EntityType}", typeof(T).Name)) //{ var entity = await Query(predicate).ProjectTo(Mapper.ConfigurationProvider).FirstOrDefaultAsync(); // op.Complete(); return entity; //} } finally { Reset(); } } public async Task FirstOrDefaultAsync(Expression> predicate, Expression> orderKeySelector) { try { return await Query(predicate).OrderByDescending(orderKeySelector).FirstOrDefaultAsync(); } finally { Reset(); } } public async Task FirstOrDefaultAsync(Expression> predicate, Expression> orderKeySelector) { try { return await Query(predicate).ProjectTo(Mapper.ConfigurationProvider).OrderByDescending(orderKeySelector).FirstOrDefaultAsync(); } finally { Reset(); } } public async Task AddAsync(T entity) { try { var currentUser = await GetCurrentUserId(); //using (var op = Logger.BeginOperation("Adding entity of type {EntityType}", typeof(T).Name)) //{ entity.CreatedById = currentUser; entity.UpdatedById = currentUser; entity.CreatedOn = DateTime.UtcNow; entity.UpdatedOn = DateTime.UtcNow; await Context.AddAsync(entity); //op.Complete(); return entity; //} } finally { Reset(); } } public async Task UpdateAsync(T entity) { try { var currentUserId = await GetCurrentUserId(); entity.UpdatedById = currentUserId; entity.UpdatedOn = DateTime.UtcNow; DbSet.Attach(entity); Context.Entry(entity).State = EntityState.Modified; return entity; } catch (Exception ex) { return entity; } finally { Reset(); } } public void Delete(T entity) { try { //using (var op = Logger.BeginOperation("Deleting entity with ID {EntityId}", entity.Id)) //{ Context.Remove(entity); // op.Complete(); //} } finally { Reset(); } } public async Task SaveChangesAsync() { try { //using (var op = Logger.BeginOperation("Saving changes!")) //{ await Context.SaveChangesAsync(); // op.Complete(); //} } finally { Reset(); } } private async Task GetUser(string username) { try { return await UserDbSet.AsNoTracking().FirstOrDefaultAsync(u => u.UserName == username); } finally { Reset(); } } private async Task GetCurrentUserId() { if (HttpContextAccessor?.HttpContext?.User?.Identity?.IsAuthenticated == true) { if (User == null) User = await GetUser(HttpContextAccessor.HttpContext.User.Identity.Name); if (User == null) return null; else return User.Id; } else return null; } private void Reset() { Modifiers.Clear(); } public void Dispose() { try { Context.Dispose(); } catch { } } } }