Skip to content

TDbContextEntityDataSetStore.ApplyChanges: one SaveChanges per item and no transaction, so a failing row leaves the batch half-applied #209

Description

@Stemonik

Summary

TDbContextEntityDataSetStore.ApplyChanges (Sources/Web/Dext.Web.EntityDataSetApi.pas) applies a batch of dataset changes one item at a time, with one SaveChanges per item and no transaction around the batch. If item 7 fails, items 1–6 are already committed, and the caller gets a per-item result list back, not a failed request.

Where

In the for i := 0 to AChanges.Count - 1 loop, each branch ends with its own ADbContext.SaveChanges (inserted, modified, deleted). The exception is caught per item and stored in ItemResult.ErrorMessage, and the loop carries on. Neither ApplyChanges nor the TEntityDataSetApi.Map<T> endpoint that calls it opens a transaction.

Why it matters

A client dataset's ApplyUpdates is usually understood as all-or-nothing: a master row plus its details, or a set of rows that only make sense together. With the current behaviour a half-applied batch is a normal outcome, and the database is left in a state no client ever asked for. It is also one round trip per item.

We learned the same lesson in our own server: batches that committed per statement looked fine until a real failure in the middle of one.

If partial success per item is intended, it may be worth documenting and making it opt-in. Something to check along the way, which we have not verified: after a failed SaveChanges, is the failed entity still in the ChangeTracker? If it is, the next item's SaveChanges would retry it.

Suggested fix

  • Wrap the loop in a transaction on ADbContext.
  • Track all items first and call SaveChanges once.
  • On failure, roll back and return the failing index.
  • If per-item results must stay, make that an explicit option rather than the default.

Checked against f440e79a.

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions