Skip to content
Shiny.Maui.Shell v6 support for AI routing tools Learn More

Removal Strategies

Shiny.DataSync provides four strategies for removing data that the client should no longer have. All are optional and composable — use any combination on the same entity.

StrategyWhen to useServer provides
Soft-DeleteServer marks entities as deleted but still returns them in pullA boolean flag on the entity (e.g. IsDeleted)
TombstoneServer tracks deleted IDs separatelystring[] of deleted IDs at a dedicated endpoint
ReconciliationNeed to catch all cases (unassigned work, bulk deletes)string[] of all valid IDs at a dedicated endpoint
ExpiryServer changes entity state to mean “not for this client anymore”A state change on the entity (e.g. AssignedTo becomes null)

The simplest approach. The server includes a deletion flag on the entity, and the library removes it locally when detected during pull.

e.SoftDeletePredicate = x => x.IsDeleted;

The server maintains a separate endpoint that returns the IDs of deleted entities. Supports incremental fetching via a date variable.

e.TombstoneUri = "/api/items/tombstones";
e.TombstoneHttpMethod = HttpMethod.Get; // default: GET
e.TombstoneDateVariable = "since"; // appends ?since=<timestamp>

Server response: string[] — an array of deleted entity IDs.

The nuclear option. The server returns all valid entity IDs, and any local entity not in the set is removed. This catches everything — bulk deletes, unassigned work orders, anything.

e.ReconciliationUri = "/api/items/ids";
e.ReconciliationHttpMethod = HttpMethod.Get; // default: GET
e.ReconciliationMinimumTime = TimeSpan.FromMinutes(30); // throttle — this is expensive

Server response: string[] — an array of all valid entity IDs.

Similar to soft-delete, but instead of a deletion flag, it detects a meaningful state change. For example, a work order that becomes unassigned from the current user.

e.ExpiryPredicate = x => x.AssignedTo == null;

You can combine strategies for maximum coverage:

ds.AddEntity<WorkOrder>(x => x.Id, e =>
{
e.PullUri = "/api/workorders";
e.PushUri = "/api/workorders";
// Real-time: catch soft-deletes and unassigned work orders during pull
e.SoftDeletePredicate = x => x.IsDeleted;
e.ExpiryPredicate = x => x.AssignedTo == null;
// Safety net: reconcile all IDs every 30 minutes
e.ReconciliationUri = "/api/workorders/ids";
e.ReconciliationMinimumTime = TimeSpan.FromMinutes(30);
});