3

I'm trying to expose an observable sequence that gives observers all existing records in a database table plus any future items. For the sake of argument, lets say it's log entries. Therefore, I'd have something like this:

public class LogService
{
    private readonly Subject<LogEntry> entries;

    public LogService()
    {
        this.entries = new Subject<LogEntry>();

        this.entries
            .Buffer(...)
            .Subscribe(async x => WriteLogEntriesToDatabaseAsync(x));
    }

    public IObservable<LogEntry> Entries
    {
        get { return this.entries; }
    }

    public IObservable<LogEntry> AllLogEntries
    {
        get
        {
            // how the heck?
        }
    }

    public void Log(string message)
    {
        this.entries.OnNext(new LogEntry(message));
    }

    private async Task<IEnumerable<LogEntry>> GetLogEntriesAsync()
    {
        // reads existing entries from DB table and returns them
    }

    private async Task WriteLogEntriesToDatabaseAsync(IList<LogEntry> entries)
    {
        // writes entries to the database
    }
}

My initial thought for the implementation of AllLogEntries was something like this:

return Observable.Create<LogEntry>(
    async observer =>
    {
        var existingEntries = await this.GetLogEntriesAsync();

        foreach (var existingEntry in existingEntries)
        {
            observer.OnNext(existingEntry);
        }

        return this.entries.Subscribe(observer);
    });

But the problem with this is that there could log entries that have been buffered and not yet written to the database. Hence, those entries will be missed because they are not in the database and have already passed through the entries observable.

My next thought was to separate the buffered entries from the non-buffered and use the buffered when implementing AllLogEntries:

return Observable.Create<LogEntry>(
    async observer =>
    {
        var existingEntries = await this.GetLogEntriesAsync();

        foreach (var existingEntry in existingEntries)
        {
            observer.OnNext(existingEntry);
        }

        return this.bufferedEntries
            .SelectMany(x => x)
            .Subscribe(observer);
    });

There are two problems with this:

  1. It means clients of AllLogEntries also have to wait for the buffer timespan to pass before they receive their log entries. I want them to see log entries instantaneously.
  2. There is still a race condition in that log entries could be written to the database between the point at which I finish reading the existing ones and the point at which I return the future entries.

So my question is: how would I actually go about achieving my requirements here with no possibility of race conditions, and avoiding any major performance penalties?

me--
  • 1,978
  • 1
  • 22
  • 42
  • If you are just looking to get notifications on when new records are added to a database table then you can look at this answer. http://stackoverflow.com/questions/15225147/watch-for-a-table-new-records-in-sql-database – bradgonesurfing Mar 26 '14 at 09:01
  • Some questions: Do log entries originate only in a single instance of the log service? Do they have means to disambiguate them (i.e. an ID) before being saved? Is there a sequence ID? – James World Mar 26 '14 at 10:03
  • @James: The log service is a singleton, yes. They do have a database-generated ID, but that is only known once they've been saved. – me-- Mar 26 '14 at 10:22
  • @brad: I'm using sqlite and want the solution to be database independent if possible. – me-- Mar 26 '14 at 10:23
  • @me-- Not sure if you can be database independant. What wrapper are you using to communicate with SQLite. On the SQLLite page I see there are about 8 different wrappers you can use for a c# to SQLite bridge http://www.sqlite.org/cvstrac/wiki?p=SqliteWrappers – bradgonesurfing Mar 26 '14 at 10:32
  • sqlite is a C library. You cannot directly call it from C#. There are many wrapper libraries available. You must be using one of them even if you are not aware of it. Some of the libraries provide an ADO.Net type interface which **may** allow you to use the .Net query notifications feature. http://msdn.microsoft.com/en-us/library/t9x04ed2(v=vs.110).aspx sqlite itself does have a datachanged callback but how that is exposed in the library you are using I cannot tell. – bradgonesurfing Mar 26 '14 at 10:38
  • @brad: I'm using SQLite.NET-PCL (https://github.com/oysteinkrog/SQLite.Net-PCL) – me-- Mar 26 '14 at 12:38
  • @me-- You might be out of luck. I scanned the test directory. https://github.com/oysteinkrog/SQLite.Net-PCL/tree/master/tests and couldn't find anything that might allow notification of database changes. I would contact the developer directly / make an issue on github with your feature request. – bradgonesurfing Mar 26 '14 at 12:49

1 Answers1

0

To do this via the client code, you will probably have to implement a solution using polling and then look for differences between calls. Possibly combining a solution with

will give you sufficient solution.

Alternatively, I'd suggest you try to find a solution where the clients are notified when the DB/table is updated. In a web application, you could use something like SignalR to do this.

For example: http://techbrij.com/database-change-notifications-asp-net-signalr-sqldependency

If its not a web-application, a similar update mechanism via sockets may work.

See these links (these came from the accepted answer of SignalR polling database for updates):

Community
  • 1
  • 1
Chris Melinn
  • 2,046
  • 1
  • 17
  • 18