在我们的Android和iOS MVVMCross应用程序中,我们偶尔会遇到SQLiteException:繁忙的异常。

给定下面的代码,我们有几个存储库,每个存储库都构造了下面的实例以及与Sqlite数据库的关联连接。假设我们有一个Stocks存储库和一个Valuations存储库,将创建两个SqliteDataService实例:类型为Stocks的SqliteDataService和类型为Valuations的SqliteDataService,它们每个都与Sqlite数据库建立连接。

对存储库的操作可能在后台线程上进行,这意味着我们可能会尝试在“估值”的同时将“库存”插入数据库。

现在,假设每个存储库都创建了自己的SqliteDataService,则connectionObject锁将仅保护相同的存储库类型以防止同时访问数据库,而不是保护Stocks和Valuations防止同时访问数据库。

我的问题是:

在每个存储库中创建连接是否有效?如果是,我们如何防范SqliteException:繁忙?

有没有更好的模式?即我们是否应该创建一个非通用的SqliteDataService类,该类在线程之间共享相同的连接?我们已经尝试过了,但是在Android上我们会遇到致命的异常。

是否有人对Xamarin MVVMCross有可靠的Sqlite DAL模式?

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);
            }
        }

最佳答案

听起来您有几种选择:

  • 仅实例化一个SqliteDataService并将其引用传递给您的Stock和Valuations对象,这似乎是最明智的,因为它们都在同一个数据库上操作
  • 实例化一个对象,以用作服务外部的锁,并将引用传递给SqliteDataService构造函数,以便两个服务共享该锁。我相信这会奏效,但我不是锁定专家。
  • 您可以在try catch块中处理Busy异常,并迭代一个计数器,以每次短时间等待数据库的最大次数,以便您有很大的连接机会。如果数据库仍然很忙,您仍然会收到异常,并且此解决方案非常麻烦。
  • 重组数据库,以使两个区域分开,这可能是不可能的,但值得考虑。
  • 关于android - SqliteException繁忙的iOS/Android Xamarin MVVMCross,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29797572/

    10-11 15:47