Search code examples
c#system.reactiveautosaverx.net

Autosave with RX in C#


I want to implement an autosave function. I have two Observables:

  • IObservable<Unit> changes: emits an item every time the user edits a text
  • IObservable<Unit> saves: emits a save event every time a save button is pressed

Now i want to combine those to into a 3rd stream writeBack. This stream has a subscriber, that writes the current text into a database.

How do i create the writeBack stream, so that it fullfills the following properties?

  • a unsaved change gets written back, if there havent occured any changes for 3 seconds (like throttle)
  • a save event writes back the last unsaved change immediately

I want to make sure to only write back unsaved changes. I dont want to save the text if:

  • there is a save event, but no changes
  • two save events in a row, but no changes in between
  • there is a save event between a change and its autosave

Solution

  • This should do it. First we identify times we may want to save:

    var saveTriggers = changes.Throttle(TimeSpan.FromSeconds(3))
        .Merge(saves);
    

    Next we filter those out to make sure they meet your logic: At least one edit between each save, and the save can't come first. The SkipWhile is just there to make sure that Saves that precede changes are ignored. The Scan counts the number of changes between each save. We only care when that number goes to 0 (indicating a save was triggered). And DistinctUntilChanged filters out consecutive saves.

    var actualSaves = saveTriggers.Select(_ => EventType.SaveTrigger)
        .Merge(changes.Select(_ => EventType.Edit))
        .SkipWhile(et => et == EventType.SaveTrigger)
        .Scan(0, (editCount, eventType) => eventType == EventType.SaveTrigger ? 0 : editCount + 1)
        .DistinctUntilChanged()
        .Where(count => count == 0)
        .Select(_ => Unit.Default);
    

    and using enum class

    enum EventType
    {
        SaveTrigger,
        Edit
    }