using Mapster; using Microsoft.EntityFrameworkCore; using Persistence.Database.Model; using Persistence.Models; using Persistence.Models.Requests; using Persistence.Repositories; namespace Persistence.Repository.Repositories; public class ChangeLogRepository : IChangeLogRepository { private DbContext db; public ChangeLogRepository(DbContext db) { this.db = db; } public async Task AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable dtos, CancellationToken token) { foreach (var dto in dtos) { var entity = CreateEntityFromDto(idAuthor, idDiscriminator, dto); db.Set().Add(entity); } var result = await db.SaveChangesAsync(token); return result; } public async Task MarkAsDeleted(Guid idEditor, IEnumerable ids, CancellationToken token) { var query = db.Set().Where(s => ids.Contains(s.Id)); var entities = await query.ToArrayAsync(token); var result = await Clear(idEditor, entities, token); return result; } public async Task MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token) { var query = db.Set() .Where(s => s.IdDiscriminator == idDiscriminator) .Where(e => e.Obsolete == null); var entities = await query.ToArrayAsync(token); var result = await Clear(idEditor, entities, token); return result; } private async Task Clear(Guid idEditor, IEnumerable entities, CancellationToken token) { var updateTime = DateTimeOffset.UtcNow; foreach (var entity in entities) { entity.Obsolete = updateTime; entity.IdEditor = idEditor; } return await db.SaveChangesAsync(token); } public async Task ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable dtos, CancellationToken token) { var result = 0; using var transaction = await db.Database.BeginTransactionAsync(token); try { result += await MarkAsDeleted(idAuthor, idDiscriminator, token); result += await AddRange(idAuthor, idDiscriminator, dtos, token); await transaction.CommitAsync(token); return result; } catch { await transaction.RollbackAsync(token); throw; } } public async Task UpdateRange(Guid idEditor, IEnumerable dtos, CancellationToken token) { var dbSet = db.Set(); var updatedIds = dtos.Select(d => d.Id); var updatedEntities = dbSet .Where(s => updatedIds.Contains(s.Id)) .ToDictionary(s => s.Id); var result = 0; using var transaction = await db.Database.BeginTransactionAsync(token); try { foreach (var dto in dtos) { var updatedEntity = updatedEntities.GetValueOrDefault(dto.Id); if(updatedEntity is null) { throw new ArgumentNullException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto)); } var newEntity = CreateEntityFromDto(idEditor, updatedEntity.IdDiscriminator, dto); dbSet.Add(newEntity); updatedEntity.IdNext = newEntity.Id; updatedEntity.Obsolete = DateTimeOffset.UtcNow; updatedEntity.IdEditor = idEditor; } result = await db.SaveChangesAsync(token); await transaction.CommitAsync(token); return result; } catch { await transaction.RollbackAsync(token); throw; } } public async Task> GetByDate( Guid idDiscriminator, DateTimeOffset momentUtc, SectionPartRequest filterRequest, PaginationRequest paginationRequest, CancellationToken token) { var query = BuildQuery(idDiscriminator, momentUtc, filterRequest); var result = await BuildPaginationContainer(query, paginationRequest, token); return result; } private IQueryable BuildQuery(Guid idDiscriminator, DateTimeOffset momentUtc, SectionPartRequest request) { var query = db.Set() .Where(e => e.IdDiscriminator == idDiscriminator) .Where(e => e.Creation <= momentUtc) .Where(e => e.Obsolete == null || e.Obsolete >= momentUtc); if (request.IdSection.HasValue) { query = query.Where(e => e.IdSection == request.IdSection); } if (request.DepthStart.HasValue) { query = query.Where(e => e.DepthStart >= request.DepthStart); } if (request.DepthEnd.HasValue) { query = query.Where(e => e.DepthEnd <= request.DepthEnd); } return query; } public async Task> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) { var query = db.Set().Where(s => s.IdDiscriminator == idDiscriminator); var min = new DateTimeOffset(dateBegin.Year, dateBegin.Month, dateBegin.Day, 0, 0, 0, TimeSpan.Zero); var max = new DateTimeOffset(dateEnd.Year, dateEnd.Month, dateEnd.Day, 0, 0, 0, TimeSpan.Zero); var createdQuery = query.Where(e => e.Creation >= min && e.Creation <= max); var editedQuery = query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max); query = createdQuery.Union(editedQuery); var entities = await query.ToArrayAsync(token); var dtos = entities.Select(e => e.Adapt()); return dtos; } private async Task> BuildPaginationContainer(IQueryable query, PaginationRequest request, CancellationToken token) { var result = new PaginationContainer { Skip = request.Skip, Take = request.Take, Items = Enumerable.Empty(), Count = await query.CountAsync(token) }; if (!String.IsNullOrEmpty(request.SortSettings)) { query = query.SortBy(request.SortSettings); } else { query = query .OrderBy(e => e.IdSection) .ThenBy(e => e.DepthStart) .ThenBy(e => e.DepthEnd); } var entities = await query .Skip(result.Skip) .Take(result.Take) .ToArrayAsync(token); var dtos = entities.Select(e => e.Adapt()); result.Items = dtos; return result; } public async Task> GetDatesChange(Guid idDiscriminator, CancellationToken token) { var query = db.Set().Where(e => e.IdDiscriminator == idDiscriminator); var datesCreateQuery = query .Select(e => e.Creation) .Distinct(); var datesCreate = await datesCreateQuery.ToArrayAsync(token); var datesUpdateQuery = query .Where(e => e.Obsolete != null) .Select(e => e.Obsolete!.Value) .Distinct(); var datesUpdate = await datesUpdateQuery.ToArrayAsync(token); var dates = Enumerable.Concat(datesCreate, datesUpdate); var datesOnly = dates .Select(d => new DateOnly(d.Year, d.Month, d.Day)) .Distinct() .OrderBy(d => d); return datesOnly; } private ChangeLog CreateEntityFromDto(Guid idAuthor, Guid idDiscriminator, DataWithWellDepthAndSectionDto dto) { var entity = new ChangeLog() { Id = default, Creation = DateTimeOffset.UtcNow, IdAuthor = idAuthor, IdDiscriminator = idDiscriminator, IdEditor = idAuthor, Value = dto.Value, IdSection = dto.IdSection, DepthStart = dto.DepthStart, DepthEnd = dto.DepthEnd, }; return entity; } public async Task> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token) { var date = dateBegin.ToUniversalTime(); var query = this.db.Set() .Where(e => e.IdDiscriminator == idDiscriminator) .Where(e => e.Creation >= date || e.Obsolete >= date); var entities = await query.ToArrayAsync(token); var dtos = entities.Select(e => e.Adapt()); return dtos; } public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) { var query = db.Set() .Where(e => e.IdDiscriminator == idDiscriminator) .GroupBy(e => 1) .Select(group => new { Min = group.Min(e => e.Creation), Max = group.Max(e => e.Creation), }); var values = await query.FirstOrDefaultAsync(token); if(values is null) { return null; } return new DatesRangeDto { From = values.Min, To = values.Max, }; } }