Skip to content

Let export and import be cancelled - #925

Merged
KrzysztofPajak merged 2 commits into
developfrom
fix/export-import-cancellation
Oct 16, 2026
Merged

KrzysztofPajak merged 2 commits into
developfrom
fix/export-import-cancellation

Conversation

@KrzysztofPajak

@KrzysztofPajak KrzysztofPajak commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Type: bugfix

Issue

Of the export/import contracts, only IRowImport<T>.Import took a CancellationToken. IExportManager<T>.Export, ISchemaProperty<T>.GetProperties, IImportManager<T>.Import, IImportDataProvider.Convert<T> and IImportDataObject<T>.Execute did not, and the panel's export and ImportExcel actions never passed HttpContext.RequestAborted. A large spreadsheet import kept running row by row, with picture downloads of up to 30 s each, after the proxy had timed out or the admin had closed the tab. A large export kept loading pictures and building a workbook nobody would download.

Solution

  • The five methods above take CancellationToken cancellationToken = default, and the token is passed all the way down:
    • export: ExportManager → ISchemaProperty.GetProperties → ExportPictures.Preload, which checks the token before each picture. A cancelled export does not build the workbook.
    • import: ImportManager → ExcelImportProvider.Convert → PowerExcelMapper.FetchAsync (CopyToAsync) → IImportDataObject.Execute → ImportRows.RunBatch → the brand, category, collection and product picture download. The country import checks the token before each row.
  • A row downloads its pictures before it writes anything, and the writes after that do not take the token. A cancel that lands in a row therefore leaves it either untouched (reported with NotProcessedError) or fully written; there is no half-saved entity without its slug or pictures. The brand, category and collection imports share one helper (ImportPictures) instead of three copies of LoadPicture; the product import compares pictures with it too.
  • DownloadUrl.DownloadImage takes the token and links it with its 30 s limit. The time limit still returns null. A cancelled caller gets OperationCanceledException, so the row is not saved as if the picture were missing.
  • RunBatch treats only an OperationCanceledException of its own token as a cancel, so a driver or HTTP timeout is still reported as a failure. IImportDataObject.Execute logs the rows it did not reach and then throws OperationCanceledException, so the panel does not report a cancelled import as imported (the country import already threw).
  • The admin controllers (Brand, Category, Collection, Product, CustomerManagement, Order, Country) pass HttpContext.RequestAborted. GetCustomerDataHandler passes the handler's token.
  • BaseController.IsRequestAborted(exception): the export/import actions' catch (Exception) leaves out the request's own cancellation, so the exception handler middleware logs the aborted request at debug level instead of the action logging an error and queueing an error notification.
  • Out of scope: running the import as a background job, which is what really fixes proxy timeouts. A token only stops the work nobody is waiting for.

Breaking changes

This extends the ISchemaProperty break from #923 in the same release (2.5.0).

  • IExportManager<T>.Export, ISchemaProperty<T>.GetProperties, IImportManager<T>.Import, IImportDataProvider.Convert<T> and IImportDataObject<T>.Execute have a new CancellationToken cancellationToken = default parameter. There are no compatibility overloads.
  • Source code that only calls these methods still compiles. A plugin that implements one of these interfaces, or subclasses ExportManager<T> or a *SchemaProperty and overrides Export/GetProperties, has to add the parameter.
  • DownloadUrl.DownloadImage has a new optional cancellationToken parameter. PowerExcelMapper.FetchAsync has a new optional parameter after valueParser.
  • A plugin built against the old signatures has to be rebuilt. Otherwise it fails at runtime with MissingMethodException or TypeLoadException.
  • IImportDataObject<T>.Execute throws OperationCanceledException when its token is cancelled; rows already saved stay saved.
  • Row API behavior: when the batch time limit fires during a picture download, the row now comes back as NotProcessedError and nothing of it is written. Before, it finished the download first, which took up to 30 s.

Testing

  1. dotnet test on Grand.Business.Catalog.Tests, Grand.Business.Common.Tests, Grand.Business.Checkout.Tests, Grand.Business.Customers.Tests, Grand.Web.Admin.Tests and Grand.Web.Tests: all pass. New tests:
    • ProductImportDataObjectTests.Execute_CancelledMidBatch_RemainingRowsLoggedAsNotProcessedAndThrows
    • ProductImportDataObjectTests.Import_CancelledDuringPictureDownload_RowNotWrittenAndNotProcessed
    • BaseBrandControllerTests.ImportFromXlsx_RequestAborted_PropagatesWithoutNotification
    • BaseBrandControllerTests.ExportXlsx_CancelledWithoutAbortedRequest_ShowsErrorAndRedirectsToList
    • SchemaPropertyTests.Product_CancelledDuringPicturePreload_StopsBeforeNextPicture
    • DownloadUrlTests.DownloadImage_CallerCancelled_ThrowsInsteadOfReturningNull
  2. Admin → Catalog → Products → Import from Excel with a large file, then close the tab: the log shows the spreadsheet-import warning listing the rows that were not processed, and no further products are written.
  3. Export to Excel for products, brands, categories, collections, orders, customers and countries works as before.

Thread a CancellationToken through IExportManager, ISchemaProperty,
IImportManager, IImportDataProvider and IImportDataObject down to the
picture preload, the spreadsheet read, the row batch and the picture
download. The admin panel passes HttpContext.RequestAborted, so a closed
tab or a proxy timeout stops the work at the next row or picture.
Copilot AI balanced review requested due to automatic review settings October 15, 2026 18:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

A row downloads its pictures before it writes anything, so a cancelled
download leaves it untouched and it is reported not processed. The
brand, category and collection imports share one picture helper instead
of three copies of LoadPicture. A cancelled panel import throws instead
of being reported as imported, and the admin export/import actions let
an aborted request's cancellation reach the exception handler instead of
logging it as an error.
@KrzysztofPajak
KrzysztofPajak merged commit 5da1af4 into develop Oct 16, 2026
6 checks passed
@KrzysztofPajak
KrzysztofPajak deleted the fix/export-import-cancellation branch October 16, 2026 04:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants