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<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<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