MA: 4.2.3 (issue below has been occuring on previous versions as well)
MonoDev: 3.0.3.2 (Mac)

This is a refinement of a previously posted issue which I have since
narrowed down.

I am using sqlite.net within my application.  I have a data update routine
to to insert and update entries which runs on a background thread of my
starter activity which displays the update progress. On lower end devices
(Galaxy ace) I am getting intermittent (averaging around 1 in 7 full update
routines) null reference exceptions with no stack trace and that are not
caught by try catch.  These do not appear to repeat on higher end devices or
emulator. Typical example of log:

D/MD-DataProvider( 5056): insert starting trans
D/MD-DataProvider( 5056): insert started trans
I/mono    ( 5056): Stacktrace:
I/mono    ( 5056): 
E/mono    ( 5056): 
E/mono    ( 5056): Unhandled Exception: System.NullReferenceException:
Object reference not set to an instance of an object

These are intermittent  but always seems to occur at the point of db calls
(deduced from copious logging with one exception which I have noted below*),
but despite many attempts with logging I have not been able to pin them down
within the sqlite code. I have taken a look through the xamarin mobile world
conference app (as this also uses sqlite.net) to see if I could find a
solution there as I have been running out of ideas. I have brought my sqlite
wrapper code inline with the mw app
(https://github.com/xamarin/mobile-samples/blob/904e5f596e196af7e4191edfda8507229e9d078a/MWC/MWC.Core/DL/MwcDatabase.cs)
as it was already fairly similar and am using the exact same version of
sqlite.net (having previously being on the most up to date).  I have a
number of single calls such as the one below that can trigger this, and a
generic insert/update routine also below (being run 10 times in a full
update, inserting/updating 10 - 500 items on each call but it seems to fail
as much on the lower numbers as higher).  Within the later of these it can
occur on any of the db calls however is more regular on the GetIds<T> call.
Also below is my sqlite wrapper code.

Any suggestion as to code changes or anything else I can try to debug this
would be gratefully received.



Call to start update process in onCreate of home activity (_dataprovider
being declared as a variable within my activity):
new Thread(new ThreadStart(() => {
_dataprovider.CheckCacheAge();})).Start();

Single db call within update routine that can trigger exception:

this.AreaList = Repository.GetAllOrdered<Area,string>(e => e.Name);

Generic insert/update method within the update routine that triggers
exception (being run 10 times in a full update), the wrapping try catch
block however never catches (occurs more often on GetIds<T>() however on a
first update this actually is only returning an empty list).  *Once I have
had it die straight after the final log call ("Log.Debug(_logTag,"completed
insert routine")"), the next line of code as it steps out of this method
would be another log call which never happens:

protected void InsertUpdateEntities<T>(IList<T> entities, string
processStageName, string processStageDisplay, bool deleteExpiredEntities)
where T : class, IEntity, new()
                {
                        try{

                        Log.Debug(_logTag,"started insert");

                        UpdateProgressChangedEvenArgs e = new
UpdateProgressChangedEvenArgs(processStageName, processStageDisplay, 0);

                        Log.Debug(_logTag," insert getting ids");
                        
                        IList<string> existingIds = Repository.GetIds<T>();

                        Log.Debug(_logTag,"insert got ids");
                        
                        int itemsToUpdate = entities.Count;
                        int itemsUpdated = 0;

                        Log.Debug(_logTag,"insert starting trans");

                        Repository.BeginDbTransaction();

                        Log.Debug(_logTag,"insert started trans");
                        
                        foreach(T entity in entities)
                        {
                                try{
                                
                                if(existingIds.Contains(entity.Id))
                                        Repository.UpdateItem<T>(entity);
                                else
                                        Repository.InsertItem<T>(entity);

                                }
                                catch{
                                        Log.Error(_logTag, 
string.Format("Failed to insert {0}, 1d: {1}",
typeof(T).ToString(), entity.Id ));
                                }
                                
                                itemsUpdated ++;

                                if(onUpdateProgressChanged != null && 
itemsUpdated % 10 == 0 ||
itemsUpdated == itemsToUpdate)
                                {
                                        e.ProgressPercentage = (itemsUpdated * 
100)/itemsToUpdate;
                                        UpdateProgress(e);
                                }       
                        }

                        Log.Debug(_logTag,"insert commiting trans");

                        Repository.CommitDbTransaction();

                        Log.Debug(_logTag,"insert committed trans");
                        
                        Log.Debug(_logTag,"completed insert");
                        
                        Log.Debug(_logTag,"starting delete");
                        
                        if(deleteExpiredEntities)
                        {       
                                try{
                                string delExpired = string.Format ("delete from 
{0} where expirationdate
<> ?", typeof(T).Name);
                                int retcout = Repository.NonQuery(delExpired, 
_expirationDate);
                                }
                                catch{}
                        }
                        
                        Log.Debug(_logTag,"completed delete");
                        
                        UpdateStageComplete(processStageName);
                        
                        Log.Debug(_logTag,"completed insert routine");

                        }
                        catch
                        {
                                Log.Debug(_logTag,"Insert exception caught");
                                Repository.RollBackDbTransaction();
                                InsertUpdateEntities<T>(entities, 
processStageName, processStageDisplay,
deleteExpiredEntities);
                        }
                }



Sqlite.net wrapper code:

public class Repository : SQLiteConnection
    {
                protected static Repository Connection = null;
                static object locker = new object ();

                protected Repository(string path)
                        :base (path)
        {  }

                static Repository()
                {
                        Connection = new 
Repository(DataProvider.GetInstance().DBHelper.DBPath);
                }

   
                public static IList<string> GetIds<T>() where T : class, 
IEntity, new()
                {       
                        lock (locker) {
                                return Connection.Table<T>().Select(e => 
e.Id).ToList();
                        }
                }

                public static IList<string> 
GetIdsWhere<T>(Expression<Func&lt;T, bool>>
predExpr) where T : class, IEntity, new()
                {
                        lock (locker) {
                        return Connection.Table<T>().Where(predExpr).Select(e 
=> e.Id).ToList();
                        }
                }

                 public static IList<T> GetAll<T>() where T : class, IEntity, 
new()
        {               
                        lock (locker) { 
                        return Connection.Table<T>().ToList();
                        }
        }

                public static IList<T> GetAllOrdered<T,U>(Expression<Func&lt;T, 
U>>
orderExpr) where T : class, IEntity, new()
                {
                        lock (locker) {
                                return 
Connection.Table<T>().OrderBy(orderExpr).ToList();
                        }
                }

                  public static T InsertItem<T>(T entity) where T : class, 
IEntity, new()
        {
                        lock (locker) {
                        entity.Id = Connection.Insert(entity).ToString();       
        
                                return entity;
                        }
        }

                public static int UpdateItem<T>(T entity) where T : class, 
IEntity, new()
                {                       
                        lock (locker) {
                        return Connection.Update(entity);
                        }
                        
                }

                public static int DeleteItem<T>(T entity) where T : class, 
IEntity, new()
        {               
                        lock (locker) {
                                return Connection.Delete<T>(entity);
                        }
        }

                public static int Delete<T>() where T : class, IEntity, new()
                {       
                        lock (locker) {
                                return Connection.Execute(string.Format("Delete 
From {0}",
typeof(T).Name), string.Empty);
                        }       
                }

                public static int Count<T>() where T : class, IEntity, new()
                {
                        lock (locker) {
                        return Connection.Table<T>().Count();
                        }
                }

                public static IList<T> QueryDb<T>( string query, params 
object[] args)
where T : class, IEntity, new()
        {       
                        lock (locker) {
                   return Connection.Query<T>(query, args).ToList();
                        }
        }

                public static void BeginDbTransaction()
                {
                        lock (locker) {
                        if(Connection.IsInTransaction)
                                Connection.Commit();
                        
                        Connection.BeginTransaction();
                        
                        }
                }
                public static void CommitDbTransaction()
                {
                        lock (locker) {
                        if(Connection.IsInTransaction)
                                Connection.Commit();
                        }
                        
                }

                public static void RollBackDbTransaction()
                {
                        lock (locker) {
                        if(Connection.IsInTransaction)
                                Connection.Rollback();
                        }
                        
                }

                public static int NonQuery( string query, params object[] args)
        {
                        lock (locker) {
                   return Connection.Execute(query, args);
                        }
        }


    }






--
View this message in context: 
http://mono-for-android.1047100.n5.nabble.com/Intermmitent-null-reference-exception-seemingly-on-sqlite-access-tp5710649.html
Sent from the Mono for Android mailing list archive at Nabble.com.
_______________________________________________
Monodroid mailing list
Monodroid@lists.ximian.com

UNSUBSCRIBE INFORMATION:
http://lists.ximian.com/mailman/listinfo/monodroid

Reply via email to