What
We want changes to be able to dispatch other changes. For example deletes:
public class DeleteEntryChange(Guid entityId) : EditChange<Entry>(entityId), IPolyType
{
public static string TypeName => "delete:Entry";
public override async ValueTask ApplyChange(Entry entry, IChangeContext context)
{
entry.DeletedAt = context.Commit.DateTime;
await foreach (var obj in context.GetObjectsReferencing(EntityId, includeDeleted: false))
{
context.DispatchChange(new RemoveReferenceChange(obj));
}
}
}
or when we want to update objects which contain a copy of data
public class UpdateHeadwordChange(Guid entityId, string headword) : EditChange<Entry>(entityId), IPolyType
{
public static string TypeName => "update:Entry:Headword";
public override async ValueTask ApplyChange(Entry entry, IChangeContext context)
{
entry.Headword = headword;
await foreach(var complexForm in context.GetObjectsReferencing(entry.Id).OfType<ComplexForm>())
{
context.DispatchChange(new UpdateComplexFormChange(complexForm.Id, headword));
}
}
}
this would also be used for data migrations, maybe previously a field was owned by an object, but you want to move that field to a different object, however you have old changes which reference the old object, you can now dispatch a change to the correct object.
Why
- simplify delete handling code. Much of our current delete code could be moved into 2 dedicated types, one for a delete and one for removing a reference.
- allow updating derived data, eg writing system exemplars, or complex forms.
- data migrations, right now if you want to move a field from one object to another there's no way to write a change which allows that because a change can only modify the object it references in it's EntityId.
How
Add a new API to IChangeContext:
|
public interface IChangeContext |
|
{ |
|
public CommitBase Commit { get; } |
|
ValueTask<IObjectSnapshot?> GetSnapshot(Guid entityId); |
|
public async ValueTask<T?> GetCurrent<T>(Guid entityId) where T : class |
|
{ |
|
var snapshot = await GetSnapshot(entityId); |
|
if (snapshot is null) return null; |
|
return (T) snapshot.Entity.DbObject; |
|
} |
|
|
|
public async ValueTask<bool> IsObjectDeleted(Guid entityId) => (await GetSnapshot(entityId))?.EntityIsDeleted ?? true; |
|
internal IObjectBase Adapt(object obj); |
|
IAsyncEnumerable<object> GetObjectsReferencing(Guid entityId, bool includeDeleted = false); |
|
IAsyncEnumerable<T> GetObjectsOfType<T>(string jsonTypeName, bool includeDeleted = false) where T : class; |
|
} |
void DispatchChanges(params Span<IChange> changes);
this would require a lot of changes to SnapshotWorker, after a change was applied we need to gather the changes it dispatched and apply them. Much of the code in MarkDeleted will prove a useful model I suspect, however MarkDeleted itself should go away entirely as it will be handled via changes now instead.
What
We want changes to be able to dispatch other changes. For example deletes:
or when we want to update objects which contain a copy of data
this would also be used for data migrations, maybe previously a field was owned by an object, but you want to move that field to a different object, however you have old changes which reference the old object, you can now dispatch a change to the correct object.
Why
How
Add a new API to IChangeContext:
harmony/src/SIL.Harmony.Core/IChangeContext.cs
Lines 3 to 18 in 5a660d1
this would require a lot of changes to
SnapshotWorker, after a change was applied we need to gather the changes it dispatched and apply them. Much of the code inMarkDeletedwill prove a useful model I suspect, howeverMarkDeleteditself should go away entirely as it will be handled via changes now instead.