Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

NullReferenceException in apps on Windows 8 RTM #91

Open
tomsdev opened this Issue · 9 comments

5 participants

@tomsdev

I migrate an app from windows 8 RP to RTM.

I didn't have problem on RP but on RTM SQLite throw very often an exception when my app launch.

Here is the exception details:

  System.NullReferenceException was unhandled by user code
  HResult=-2147467261
  Message=Object reference not set to an instance of an object.
  Source=mscorlib
  StackTrace:
       at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
       at System.Collections.Generic.Dictionary`2.set_Item(TKey key, TValue value)
       at SQLite.SQLiteConnection.GetMapping(Type type) in \SQLite.cs:line 234
       at SQLite.SQLiteConnection.CreateTable(Type ty) in \SQLite.cs:line 308
       at SQLite.SQLiteAsyncConnection.<>c__DisplayClass1.<CreateTablesAsync>b__0() in \SQLiteAsync.cs:line 

94
       at System.Threading.Tasks.Task`1.InnerInvoke()
       at System.Threading.Tasks.Task.Execute()

Can you investigate on it?

@timheuer
Collaborator
@tomsdev

When i have time i'll isolate the bug in a project but perhaps others have a similar issue so this is a general bug with RTM.

Anyway, it's a simple table of entity (like Person in the wiki) and i just read all them from the table when the app launch.

@jamiepenney

I have the same problem. It happens when I try to call CreateTableAsync like so:

var connection = new SQLiteAsyncConnection("music");
await connection.CreateTableAsync<LibraryEntry>();

And LibraryEntry is this:

    public class LibraryEntry
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }

        public LibraryEntry() {}

        public LibraryEntry(string path)
        {
            Path = path;
        }

        public string Path   { get; private set; }
        public string Artist { get; set; }
        public string Album  { get; set; }
        public string Title  { get; set; }
        public uint TrackNumber { get; set; }
    }

The Create table call is in an async method that gets called on app startup. I have the same stack trace as the OP.

@timheuer
Collaborator
@jamiepenney
var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
var connection = new SQLiteAsyncConnection(Path.Combine(folder.Path, "music.db"));

Changed it to this and that NullReferenceException went away. I was following a tutorial which said I just needed a db name, this makes more sense.

Not sure why the error seemed so unrelated to the cause though?

@jamiepenney

Ok so that didn't fix the error. It turns out that by just using "music" as the path, it was writing the file to the LocalFolder anyway
localfolder
It seems to only work the first time we run the app and create the database. If it tries to run again, it fails with the original error. I can replicate this by deleting the db file and rerunning the app.

This is the full extent of my interactions with the database:

    public class MusicDatabase
    {
        private LibraryViewModel _currentLibrary;
        private readonly AwaitableCriticalSection _criticalSection = new AwaitableCriticalSection();
        private volatile SQLiteAsyncConnection _connection;
        private readonly object _connectionLock = new object();

        public async void CreateDb()
        {
            var connection = GetConnection();
            await connection.CreateTableAsync<LibraryEntryViewModel>();
        }

        /// <summary>
        /// Connects to the database, loads the full music library from it, then attaches event handlers to that
        /// instance to do automatic inserts/deletes when new items are added/deleted
        /// </summary>
        /// <returns></returns>
        public async Task<LibraryViewModel> LoadFullLibrary()
        {
            if (_currentLibrary != null)
            {
                _currentLibrary.EntryAdded -= Library_OnEntryAdded;
                _currentLibrary.EntryRemoved -= Library_OnEntryRemoved;
                _currentLibrary = null;
            }
            else
            {
                // On first run, attempt to create the database tables if they don't already exist.
                CreateDb();    
            }

            var connection = GetConnection();
            var entries = await connection.Table<LibraryEntryViewModel>().ToListAsync();
            _currentLibrary = new LibraryViewModel(entries);

            _currentLibrary.EntryAdded += Library_OnEntryAdded;
            _currentLibrary.EntryRemoved += Library_OnEntryRemoved;

            return _currentLibrary;
        }

        private async void Library_OnEntryAdded(LibraryViewModel sender, LibraryEntryEventArgs args)
        {
            using (await _criticalSection.EnterAsync())
            {
                var connection = GetConnection();
                await connection.InsertAsync(args.Entry);
            }
        }

        private async void Library_OnEntryRemoved(LibraryViewModel sender, LibraryEntryEventArgs args)
        {
            using (await _criticalSection.EnterAsync())
            {
                var connection = GetConnection();
                await connection.DeleteAsync(args.Entry);
            }
        }

        private SQLiteAsyncConnection GetConnection()
        {
            var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
            return new SQLiteAsyncConnection(Path.Combine(folder.Path, "music.db"));
        }
    }
@jamiepenney

Ok now I've figured it out. TheSQLite.SQLiteConnection.GetMapping(Type) method isn't threadsafe, and there is nothing in SQLiteAsync stopping it from running concurrently. I've put a critical section around my LoadFullLibrary code, and will make sure there is one around all db operations.

@tyczj

I have the same problem too with the GetMapping I had to put a lock in the method for it to stop giving me the exception

@stevebgit

http://channel9.msdn.com/Events/TechEd/NorthAmerica/2013/WPH-B306#fbid=

At about 34:00, in Andy Wigley's video above, there is what appears to be a good solution to avoiding the async race condition problem, when accessing a sqlite db ( even though he's lecturing about SqliteRT).

The GetDatabaseAsync() function, specifically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.