public async Task ExportToDocumentAsync <TModel>(string path, IReadOnlyList <ResGroupModel <TModel> > groups, IStatusProgress progress, CancellationToken cancellationToken) where TModel : IRowModel { progress.Report(StatusRes.GettingSpreadsheet); SpreadsheetFeed spreadSheets = await Service.GetFeedAsync(new SpreadsheetQuery(path), progress, cancellationToken); SpreadsheetEntry spreadsheet = (SpreadsheetEntry)spreadSheets.Entries.First(); progress.Report(StatusRes.GettingWorksheets); WorksheetFeed worksheetsFeed = await Service.GetFeedAsync(spreadsheet.GetWorkSheetQuery(), progress, cancellationToken); int columsCount = groups.First().Tables.First().Rows.First().DataList.Count; List <KeyValuePair <string, WorksheetEntry> > newWorksheets = groups .Select(g => new KeyValuePair <string, WorksheetEntry>(g.GroupTitle, new WorksheetEntry((uint)(g.Tables.Sum(t => t.Rows.Count + 3) + /*Table name rows*/ g.Tables.Count - 1), (uint)columsCount, g.GroupTitle))) .ToList(); List <WorksheetEntry> worksheets2Rename = worksheetsFeed.Entries.Cast <WorksheetEntry>() .Where(ws => newWorksheets.Any(nws => String.Equals(nws.Key, ws.Title.Text, StringComparison.OrdinalIgnoreCase))) .ToList(); if (worksheets2Rename.Count != 0) { foreach (var worksheetEntry in worksheets2Rename) { worksheetEntry.Title.Text = Guid.NewGuid().ToString("N"); worksheetEntry.BatchData = new GDataBatchEntryData(GDataBatchOperationType.update); } progress.Report(StatusRes.RenamingOldWorksheets); var progresses = progress.CreateParallelProgresses(worksheets2Rename.Count); //Renaming worksheets with matching names. await Task.WhenAll(worksheets2Rename.Zip(progresses, (ws, p) => Service.UpdateItemAsync(ws, p, cancellationToken))); } progress.Report(StatusRes.InsertingNewWorksheets); //Creating new worksheets. var insertingProgresses = progress.CreateParallelProgresses(newWorksheets.Count); var createdWorksheets = await Task.WhenAll(newWorksheets.Zip(insertingProgresses, (kvp, p) => Service.InsertItemAsync(spreadsheet.GetWorkSheetLink(), kvp.Value, p, cancellationToken))); newWorksheets = createdWorksheets.Select(ws => new KeyValuePair <string, WorksheetEntry>(ws.Title.Text, ws)).ToList(); progress.Report(StatusRes.DeletingOldWorksheets); //Clearing of previous worksheets. var deletingProgresses = progress.CreateParallelProgresses(worksheetsFeed.Entries.Count); await Task.WhenAll(worksheetsFeed.Entries.Cast <WorksheetEntry>().Zip(deletingProgresses, (ws, p) => Service.DeleteItemAsync(ws, p, cancellationToken)).ToArray()); progress.Report(StatusRes.PushingCells); var groupWorksheetsJoin = newWorksheets.Join(groups, ws => ws.Key, g => g.GroupTitle, (kvp, group) => new { Group = group, Worksheet = kvp.Value }).ToList(); var groupProgresses = progress.CreateParallelProgresses(groupWorksheetsJoin.Select(j => (double)j.Worksheet.Cols * j.Worksheet.Rows).ToList()); SemaphoreSlim semaphore = new SemaphoreSlim(MaxPushRequestsCount); await Task.WhenAll(groupWorksheetsJoin.Zip(groupProgresses, (j, p) => PushCellsAsync(j.Worksheet, j.Group, semaphore, p, cancellationToken))); }