public async Task <IReadOnlyCollection <ObjectMetadataRecord> > GetObjectMetadatas(IReadOnlyCollection <long> ids)
        {
            var uniqueIds   = new HashSet <long>(ids);
            var partitioner = Partitioner.Create(uniqueIds);
            var result      = new ObjectMetadataRecord[uniqueIds.Count];
            var tasks       = partitioner
                              .GetOrderablePartitions(_degreeOfParallelism)
                              .Select(async x =>
            {
                while (x.MoveNext())
                {
                    var id = x.Current.Value;
                    ObjectMetadataRecord record;
                    try
                    {
                        var objectVersions = await GetObjectLatestVersions(id);
                        var versionId      = objectVersions.Where(v => v.Id.EndsWith(Tokens.ObjectPostfix))
                                             .Select(v => v.VersionId)
                                             .SingleOrDefault();
                        if (versionId == null)
                        {
                            record = null;
                        }
                        else
                        {
                            var response        = await _s3Client.GetObjectMetadataAsync(_bucketName, id.AsS3ObjectKey(Tokens.ObjectPostfix), versionId);
                            var metadataWrapper = MetadataCollectionWrapper.For(response.Metadata);
                            var author          = metadataWrapper.Read <string>(MetadataElement.Author);
                            var authorLogin     = metadataWrapper.Read <string>(MetadataElement.AuthorLogin);
                            var authorName      = metadataWrapper.Read <string>(MetadataElement.AuthorName);

                            record = new ObjectMetadataRecord(
                                id,
                                versionId,
                                response.LastModified,
                                new AuthorInfo(author, authorLogin, authorName));
                        }
                    }
                    catch (AmazonS3Exception ex) when(ex.StatusCode == HttpStatusCode.NotFound)
                    {
                        record = null;
                    }

                    result[x.Current.Key] = record;
                }
            });
            await Task.WhenAll(tasks);

            return(result.Where(x => x != null).ToList());
        }
        public async Task <IReadOnlyCollection <ObjectMetadataRecord> > GetTemplateMetadatas(IReadOnlyCollection <long> ids)
        {
            var uniqueIds   = new HashSet <long>(ids);
            var partitioner = Partitioner.Create(uniqueIds);
            var result      = new ObjectMetadataRecord[uniqueIds.Count];
            var tasks       = partitioner
                              .GetOrderablePartitions(_degreeOfParallelism)
                              .Select(async x =>
            {
                while (x.MoveNext())
                {
                    var templateId = x.Current.Value;
                    ObjectMetadataRecord record;
                    try
                    {
                        var versionId = await GetTemplateLatestVersion(templateId);

                        var response        = await _s3Client.GetObjectMetadataAsync(_bucketName, templateId.ToString(), versionId);
                        var metadataWrapper = MetadataCollectionWrapper.For(response.Metadata);
                        var author          = metadataWrapper.Read <string>(MetadataElement.Author);
                        var authorLogin     = metadataWrapper.Read <string>(MetadataElement.AuthorLogin);
                        var authorName      = metadataWrapper.Read <string>(MetadataElement.AuthorName);

                        record = new ObjectMetadataRecord(
                            templateId,
                            versionId,
                            response.LastModified,
                            new AuthorInfo(author, authorLogin, authorName));
                    }
                    catch (AmazonS3Exception ex) when(ex.StatusCode == HttpStatusCode.NotFound)
                    {
                        record = null;
                    }
                    catch (ObjectNotFoundException)
                    {
                        record = null;
                    }

                    result[x.Current.Key] = record;
                }
            });

            await Task.WhenAll(tasks);

            return(result.Where(x => x != null).ToList());
        }