2

I load a list from a SQLite database when my page loads and sometimes when it loads I get NullReferenceException with the error saying Object reference not set to an instance of an object.

it breaks in this code in the SQLite class file

public TableMapping GetMapping (Type type)
{
    if (_mappings == null) {
        _mappings = new Dictionary<string, TableMapping> ();
    }
    TableMapping map;
    if (!_mappings.TryGetValue (type.FullName, out map)) {
        map = new TableMapping (type);
        _mappings [type.FullName] = map; //null here
    }
    return map;
}

This is what I do when my page loads

public MainPage()
{
    this.InitializeComponent();
    Loaded += MainPage_Loaded;
}

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    createDatabase();
    getBowlers();
}

private async void createDatabase()
{
    SQLiteAsyncConnection conn = new SQLiteAsyncConnection(BOWLERS_DATABASE);
    await conn.CreateTableAsync<Bowler>();
    conn = new SQLiteAsyncConnection(GAMES_DATABASE);
    await conn.CreateTableAsync<Games>();
}

private async void getBowlers()
{
    SQLiteAsyncConnection conn = new SQLiteAsyncConnection(BOWLERS_DATABASE);

    var query = conn.Table<Bowler>();
    itemListView.DataContext = await query.ToListAsync();
}

I am new to page lifecycle but it seems that I am trying to pull from the database to early possibly?

EDIT

stacktrace

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 c:\Users\Jeff\Dropbox\Tournament Director Windows\Tournament Director Windows\SQLite.cs:line 231
   at SQLite.TableQuery`1..ctor(SQLiteConnection conn) in c:\Users\Jeff\Dropbox\Tournament Director Windows\Tournament Director Windows\SQLite.cs:line 2129
   at SQLite.SQLiteConnection.Table[T]() in c:\Users\Jeff\Dropbox\Tournament Director Windows\Tournament Director Windows\SQLite.cs:line 616
   at SQLite.SQLiteAsyncConnection.Table[T]() in c:\Users\Jeff\Dropbox\Tournament Director Windows\Tournament Director Windows\SQLiteAsync.cs:line 260
   at Tournament_Director_Windows.MainPage.<getBowlers>d__c.MoveNext() in c:\Users\Jeff\Dropbox\Tournament Director Windows\Tournament Director Windows\MainPage.xaml.cs:line 116
InnerException: 
John Saunders
  • 160,644
  • 26
  • 247
  • 397
tyczj
  • 71,600
  • 54
  • 194
  • 296

3 Answers3

4

Based on the exception and provided stack trace, I think that your issue is what is described here.

Note:

  1. The exception is thrown from the Dictionary code, not from your (user) code
  2. The exception is NullReferenceException, not ArgumentNullException, so it's not just the argument being null

Dictionary is not thread safe. This means that access to it should be synchronised if it's accessed by several threads.

UPDATE 1

It seems like there is a bug in SQLite async handling.

Community
  • 1
  • 1
Andrew Savinykh
  • 25,351
  • 17
  • 103
  • 158
  • This call is in a external library, so you need to synchronize calls to the SQLite library. – Scott Chamberlain Jul 09 '13 at 05:23
  • 2
    I wish I could +1 you twice, I bet you got it. The call to `SQLiteAsyncConnection` should not be shared among threads. Be sure [Connection Pooling](http://stackoverflow.com/questions/10703814/sqlite-c-connection-pooling-and-prepared-statement-confusion) is enabled and it will reuse the resources for you without sharing connection objects. – Scott Chamberlain Jul 09 '13 at 05:39
  • Yes this it is a threading problem, I put a `lock` in the `GetMapping` and has not crashed since – tyczj Jul 09 '13 at 13:44
1

The problem with the original code is that the GetMapping method was not thread-safe. Adding a lock, below, forces it to be thread-safe and solves the problem.

Based on the answer and comments, here is the code, change the GetMapping Method in your SQLite.cs file to this

private Object thisLock = new Object(); // add lock obj
public TableMapping GetMapping(Type type, CreateFlags createFlags = CreateFlags.None)
{
    // include original code in the lock block
    lock (thisLock)
    {
        if (_mappings == null)
        {
            _mappings = new Dictionary<string, TableMapping>();
        }
        TableMapping map;
        if (!_mappings.TryGetValue(type.FullName, out map))
        {
            map = new TableMapping(type, createFlags);
            _mappings[type.FullName] = map;
        }
        return map;
    }
}
John Saunders
  • 160,644
  • 26
  • 247
  • 397
codingrhythm
  • 1,456
  • 14
  • 26
  • Please say what changed. – John Saunders May 12 '15 at 01:14
  • @JohnSaunders it's based on the accepted answer of this question which does not provide code. So I searched how to implement the lock and tried out myself for several days, no exceptions so far. I think it would be nice to contribute the code to this question, so people who is new to .net development would not need to search again. I think the reason is quite obvious though. – codingrhythm May 13 '15 at 06:26
  • Great. Now say in your answer that based on the answer and comments, the problem was that the code is not thread-safe, and that adding a lock makes it thread-safe. – John Saunders May 13 '15 at 07:27
  • @JohnSaunders then what fix should be done to solve this problem? and why the downvote without any suggestion? The comment by tyczj in the accepted answer suggested the lock solution. So the code implement of the lock solution which helps me does not provide any help to others? – codingrhythm May 13 '15 at 23:35
  • I suggested that you update to say that the problem was that the code was not thread-safe, and that now it is. I would then have upvoted. I'll do that for you, then upvote. – John Saunders May 14 '15 at 02:44
0

Try this:

if (!_mappings.ContainsKey(type.FullName))
{
    _mappings.Add(map);
}

_mappings [type.FullName] = map;