Skip to content
Introducing AI Conversations: Natural Language Interaction for Your Apps! 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);
});