private async Task <IReadOnlyCollection <ObjectVersionRecord> > GetObjectVersions(long id, string initialVersionId, bool fetchElements) { await _distributedLockManager.EnsureLockNotExistsAsync(id); var query = _context.Objects .AsNoTracking() .Where(x => x.Id == id) .OrderByDescending(x => x.VersionIndex) .AsQueryable(); if (fetchElements) { query = query.Include(x => x.ElementLinks) .ThenInclude(x => x.Element); } var allVersions = await query.ToListAsync(); if (allVersions.Count == 0) { throw new ObjectNotFoundException($"Object '{id}' not found."); } var versions = allVersions.TakeWhile(x => x.VersionId != initialVersionId); var records = new List <ObjectVersionRecord>(allVersions.Count); foreach (var version in versions) { var elements = fetchElements ? version.ElementLinks.Select(x => x.Element) : Array.Empty <ObjectElement>(); var descriptor = GetObjectDescriptorFromEntity(version, elements); records.Add(new ObjectVersionRecord( version.Id, version.VersionId, version.VersionIndex, version.TemplateId, version.TemplateVersionId, DateTime.SpecifyKind(version.LastModified, DateTimeKind.Utc), new AuthorInfo(version.Author, version.AuthorLogin, version.AuthorName), descriptor.Properties, descriptor.Elements .Select(x => new ObjectVersionRecord.ElementRecord(x.TemplateCode, x.Value)) .ToList(), version.ModifiedElements)); } return(records); }
private async Task <IReadOnlyCollection <ObjectVersionRecord> > GetObjectVersions(long id, string initialVersionId, bool fetchElements) { var objectDescriptors = new List <ObjectDescriptor>(); async Task <(bool IsTruncated, int NextVersionIndex, string NextVersionIdMarker)> ListVersions(int nextVersionIndex, string nextVersionIdMarker) { await _distributedLockManager.EnsureLockNotExistsAsync(id); var response = await _s3Client.ListVersionsAsync( new ListVersionsRequest { BucketName = _bucketName, Prefix = id.AsS3ObjectKey(Tokens.ObjectPostfix), VersionIdMarker = nextVersionIdMarker }); var nonDeletedVersions = response.Versions.FindAll(x => !x.IsDeleteMarker); nextVersionIndex += nonDeletedVersions.Count; var initialVersionIdReached = false; var versionInfos = nonDeletedVersions .Aggregate( new List <(string VersionId, DateTime LastModified)>(), (list, next) => { initialVersionIdReached = initialVersionIdReached || (!string.IsNullOrEmpty(initialVersionId) && initialVersionId.Equals(next.VersionId, StringComparison.OrdinalIgnoreCase)); if (!initialVersionIdReached) { list.Add((next.VersionId, next.LastModified)); } return(list); }); var descriptors = new ObjectDescriptor[versionInfos.Count]; var partitioner = Partitioner.Create(versionInfos); var tasks = partitioner.GetOrderablePartitions(_degreeOfParallelism) .Select(async partition => { while (partition.MoveNext()) { var index = partition.Current.Key; var versionInfo = partition.Current.Value; descriptors[index] = await GetObjectDescriptor(id, versionInfo.VersionId, CancellationToken.None, fetchElements); } }); await Task.WhenAll(tasks); objectDescriptors.AddRange(descriptors); return(!initialVersionIdReached && response.IsTruncated, nextVersionIndex, response.NextVersionIdMarker); } var result = await ListVersions(0, null); if (objectDescriptors.Count == 0) { if (!await IsObjectExists(id)) { throw new ObjectNotFoundException($"Object '{id}' not found."); } return(Array.Empty <ObjectVersionRecord>()); } while (result.IsTruncated) { result = await ListVersions(result.NextVersionIndex, result.NextVersionIdMarker); } var maxVersionIndex = result.NextVersionIndex; var records = new ObjectVersionRecord[objectDescriptors.Count]; for (var index = 0; index < objectDescriptors.Count; ++index) { var descriptor = objectDescriptors[index]; records[index] = new ObjectVersionRecord( descriptor.Id, descriptor.VersionId, --maxVersionIndex, descriptor.LastModified, new AuthorInfo(descriptor.Metadata.Author, descriptor.Metadata.AuthorLogin, descriptor.Metadata.AuthorName), descriptor.Properties, descriptor.Elements.Select(x => new ObjectVersionRecord.ElementRecord(x.TemplateCode, x.Value)).ToList(), descriptor.Metadata.ModifiedElements); } return(records); }