简体   繁体   English

SqliteException忙着iOS / Android Xamarin MVVMCross

[英]SqliteException busy iOS/Android Xamarin MVVMCross

In our Android and iOS MVVMCross app we are experiencing occasional SQLiteException: busy exceptions. 在我们的Android和iOS MVVMCross应用程序中,我们偶尔会遇到SQLiteException:繁忙的异常。

Given the code below, we have several repositories each of which construct a instance of the below and an associated connection to the Sqlite database. 鉴于下面的代码,我们有几个存储库,每个存储库构造一个下面的实例和一个与Sqlite数据库的关联连接。 Imagine we have a Stocks Repository and a Valuations Repository, two instances of SqliteDataService will be created: SqliteDataService with type Stocks and SqliteDataService with types Valuations, each of which have a connection to the Sqlite database. 想象一下,我们有一个Stocks Repository和一个Valuations Repository,将创建两个SqliteDataService实例:SqliteDataService类型为Stocks,SqliteDataService类型为Valuations,每个都与Sqlite数据库有连接。

Actions on the repositories may operate on background threads which means that we may attempt to insert Stocks into the database at the same time as Valuations. 存储库上的操作可以在后台线程上运行,这意味着我们可能会尝试在与Valuations同时将Stocks插入数据库。

Now given each repository creates its own SqliteDataService the connectionObject lock will only protect the same repository types from accessing the database at the same time rather than protecting Stocks and Valuations from accessing the database at the same time. 现在,每个存储库创建自己的SqliteDataService,connectionObject锁将仅保护相同的存储库类型同时访问数据库,而不是保护Stocks和Valuations同时访问数据库。

My questions are: 我的问题是:

Is it valid to create a connections per repository and if so, how do we guard against SqliteException: busy? 每个存储库创建连接是否有效,如果是,我们如何防范SqliteException:busy?

Is there a better pattern? 有更好的模式吗? ie Should we create a non-generic SqliteDataService class that shares the same connection across threads? 即我们应该创建一个跨线程共享相同连接的非泛型SqliteDataService类吗? We have tried this but on Android we experience fatal exceptions. 我们尝试了这个,但在Android上我们遇到了致命的例外。

Does anybody have a solid Sqlite DAL pattern for Xamarin MVVMCross? 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);
            }
        }

It sounds like you have a few options: 听起来你有几个选择:

  1. Instantiate only a single SqliteDataService and pass a reference to it to both your Stocks and Valuations objects, this would seem most sensible as both are operating on the same DB 仅实例化一个SqliteDataService并将其引用传递给您的Stocks和Valuations对象,这似乎是最明智的,因为它们都在同一个DB上运行

  2. Instantiate an object for use as a lock outside the service and pass a reference into the SqliteDataService constructor so the lock is shared by both services. 实例化一个对象以用作服务外部的锁,并将引用传递给SqliteDataService构造函数,以便锁定由两个服务共享。 I believe this would work but I am no expert on locking. 我相信这会奏效,但我不是锁定方面的专家。

  3. You could handle the Busy exception in a try catch block and iterate a counter to make a max number of attempts against the database with a short wait each time so that you have a good chance of connecting. 您可以在try catch块中处理Busy异常,并迭代计数器以使用每次短暂的等待对数据库进行最大次数尝试,以便您有很好的连接机会。 If the DB remains busy you will still get the exception and this solution is quite a messy one. 如果数据库仍然忙,你仍然会得到异常,这个解决方案非常混乱。

  4. Restructure the DB so that the two areas are separated, this is probably not possible but worth a thought. 重构数据库,使两个区域分开,这可能是不可能的,但值得一想。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM