2015-04-22 7 views
6

W naszej aplikacji MVVMCross na Androida i iOS występują sporadyczne wyjątki SQLiteException: ruchliwe wyjątki.SqliteException zajęty iOS/Android Xamarin MVVMCross

Podając poniższy kod, mamy kilka repozytoriów, z których każdy tworzy instancję poniżej i powiązanego połączenia z bazą danych Sqlite. Wyobraźmy sobie, że mamy Repozytorium Zapasów i Repozytorium Wycen, zostaną utworzone dwa wystąpienia SqliteDataService: SqliteDataService z typem Stocks i SqliteDataService z typami Valuations, z których każdy ma połączenie z bazą danych Sqlite.

Akcje w repozytoriach mogą działać na wątkach działających w tle, co oznacza, że ​​możemy próbować wstawiać Zapasy do bazy danych w tym samym czasie co Wyceny.

Teraz, gdy każde repozytorium tworzy własną SqliteDataService, blokada connectionObject chroni tylko te same typy repozytoriów przed dostępem do bazy danych w tym samym czasie, a nie chroni zapasów i wyceny przed dostępem do bazy danych w tym samym czasie.

Moje pytania są następujące:

Czy to ważne, aby stworzyć połączeń na składowiska, a jeśli tak, to w jaki sposób ustrzec się przed SqliteException: zajęty?

Czy istnieje lepszy wzór? Czy powinniśmy utworzyć nietypową klasę SqliteDataService, która ma takie samo połączenie między wątkami? Próbowaliśmy tego, ale w systemie Android doświadczamy fatalnych wyjątków.

Czy ktoś ma stabilny Sqlite DAL dla Xamarin MVVMCross?

public class SqliteDataService<T> : IDataService<T> where T : new() 
{ 
    private static object lockObject = new object(); 

    private static object connectionObject = new object(); 

    private static ISQLiteConnection _connection; 

    private static SqliteDataService<T> _instance; 

    public SqliteDataService(ISQLiteConnectionFactory connectionFactory, string dbPath) 
    { 
     if (_connection == null) 
     { 
      _connection = connectionFactory.Create (dbPath); 
      _connection.CreateTable<T>(); 
     } 
    } 

    public static SqliteDataService<T> GetInstance(ISQLiteConnectionFactory connectionFactory, string dbPath) 
    { 

     if (_instance == null) 
     { 
      lock (lockObject) 
      { 
       _instance = new SqliteDataService<T> (connectionFactory, dbPath); 
      } 
     } 

     return _instance; 
    } 

    public void CreateTable<T>() 
    { 

    } 

    public void Insert(T value) 
    { 
     lock (connectionObject) { 
      _connection.Insert (value, typeof(T)); 
     } 
    } 

    public void InsertAll(IEnumerable<T> values) 
    { 
     lock (connectionObject) { 
      _connection.Insert (values, typeof(T)); 
     } 
    } 

    public IEnumerable<T> Read(Expression<Func<T, bool>> predicate) 
    { 
     lock (connectionObject) { 
      return _connection.Table<T>().Where (predicate); 
     } 
    } 

    public T ReadFirst(Expression<Func<T, bool>> predicate) 
    { 
     lock (connectionObject) { 
      return Read (predicate).FirstOrDefault(); 
     } 
    } 

    public void Update(T value) 
    { 
     lock (connectionObject) { 
      _connection.Update (value, typeof(T)); 
     } 
    } 

    public void Delete(Expression<Func<T, bool>> predicate) 
    { 
     lock (connectionObject) { 
      var valuesToDelete = Read (predicate); 

      if (valuesToDelete == null) 
       return; 

      foreach (var value in valuesToDelete) { 
       _connection.Delete (value); 
      } 
     } 
+0

Naprawiłeś to? Opublikuj odpowiedź, jeśli została naprawiona. –

Odpowiedz

0

Brzmi jak masz kilka opcji:

  1. Instantiate tylko jeden SqliteDataService i przekazać odniesienie do niej zarówno do zapasów i obiektów wycen, byłoby to wydaje najrozsądniejsze jak oba są działa na tym samym DB

  2. Utworzenie obiektu do użycia jako blokady poza usługą i przekazanie odwołania do konstruktora SqliteDataService, aby blokada była współdzielona przez obie usługi. Wierzę, że to zadziała, ale nie jestem ekspertem od blokowania.

  3. Można obsłużyć wyjątek Zajęty w bloku catch catch i powtórzyć licznik, aby za każdym razem podać maksymalną liczbę prób w bazie danych, aby uzyskać dużą szansę na połączenie. Jeśli DB będzie nadal zajęty, nadal otrzymasz wyjątek, a to rozwiązanie jest dość niechlujne.

  4. Restrukturyzacja DB, tak aby dwa obszary zostały rozdzielone, prawdopodobnie nie jest to możliwe, ale warto pomyśleć.

Powiązane problemy