SQLite3和C#异步的线程问题

时间:2014-01-27 21:05:51

标签: c# multithreading asynchronous sqlite

我正在尝试将一些数据保存到SQLite3数据库中。如果我不使用异步,我可以毫无问题地保存数据。但是,当我尝试使用以下代码时,我收到以下错误:

{Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack.}

在我的UI中,我调用了以下SyncDomainTablesAsync方法:

private readonly IDataCoordinator _coordinator;

public Configuration(IDataCoordinator coordinator)
{
    _coordinator = coordinator;
}

public async Task<int> SyncDomainTablesAsync(IProgress<string> progress, CancellationToken ct, DateTime? lastDateSynced=null, string tableName = null)
{
    //Determine the different type of sync calls
    // 1) Force Resync (Drop/Create Tables and Insert)
    // 2) Auto Update 

    var domainTable = await GetDomainTablesAsync(progress,ct,lastDateSynced, tableName);
    var items = domainTable.Items;

    int processCount = await Task.Run<int>( async () =>
    {
        int p = 0;
        progress.Report(String.Format("Syncing Configurations..."));

        foreach (var item in items)
        {
            progress.Report(String.Format("Syncing {0} Information",item.Key));
            var task = await SyncTableAsync(item.Value); // INVOKED BELOW
            if (task) progress.Report(String.Format("Sync'd {0} {1} records", item.Value.Count,item.Key));
            if (ct.IsCancellationRequested) goto Cancelled;
            p += item.Value.Count;
        }

        Cancelled:
        if (ct.IsCancellationRequested)
        {   
            //Update Last Sync'd Records
            progress.Report(String.Format("Canceling Configuration Sync..."));
            ct.ThrowIfCancellationRequested();
        }
        else
            progress.Report(String.Format("Syncing Configurations Compleleted"));
        return p;
    },ct);
    return processCount;
}

private async Task<bool> SyncTableAsync(IEnumerable<object> items, bool includeRelationships = false) 
{
    try
    {
        //TODO: Replace with SaveObjects method
        var i = await Task.Run(() => _coordinator.SaveObjects(items, includeRelationships));
        if (i == 0)
            return false;
    }
    catch(Exception ex)
    {
        return false;
    }

    return true;
}

UI调用SyncDomainTablesAsync方法。然后我创建一个新的Task并循环遍历从GetDomainTablesAsync方法返回的项目。在每次迭代期间,我等待SyncTableAsync方法完成。在SyncTableAsync内,我在实现我的IDataCoordinator接口的类中调用了SaveObject方法。

public override int SaveObjects(IEnumerable<object> items, Type underlyingType, bool saveRelationships = true)
{
    int result = 0;

    if (items == null)
        throw new ArgumentNullException("Can not save collection of objects. The collection is null.");
    else if (items.Count() == 0)
        return 0;

    // Check if table exists.
    foreach (var item in items)
        this.CreateTable(item.GetType(), saveRelationships);

    using (SQLiteConnection connection = new SQLiteConnection(this.StorageContainerPath))
    {
        connection.BeginTransaction();

        foreach (var item in items)
        {
            result += ProcessSave(item, saveRelationships, connection);
        }

        try
        {
            connection.Commit();
        }
        catch (SQLiteException ex)
        {
            connection.Rollback();
            throw ex;
        }
    }

    return result;
}

public override int CreateTable(Type type, bool createRelationalTables = false)
{
    if (this.TableExists(type) == 1)
        return 1;

    using (SQLiteConnection cn = new SQLiteConnection(this.StorageContainerPath))
    {
        try
        {

            // Check if the Table attribute is used to specify a table name not matching that of the Type.Name property.
            // If so, we generate a Sql Statement and create the table based on the attribute name.
            //if (Attribute.IsDefined(type, typeof(TableAttribute)))
            //{
            //    TableAttribute attribute = type.GetAttribute<TableAttribute>();
                // Strongly typed to SQLiteCoordinator just to get a SqlQuery instance. The CreateCommand method will create a table based on 'type'
                var query = new SqlQuery<SQLiteCoordinator>().CreateCommand(DataProviderTypes.Sqlite3, type);
                query = query.TrimEnd(';') + ";";

                cn.Execute(query);


            //}
            // Otherwise create the table using the Type.
            //else
            //{
            //    cn.CreateTable(type);
            //}

            // If we are to create relationship tables, we cascade through all relationship properties
            // and create tables for them as well.
            if (createRelationalTables)
            {
                this.CreateCascadingTables(type, cn);
            }
        }
        catch (Exception ex)
        {
            return 0;
        }
    }

    return 1;
}

代码流程

UI-&GT; SyncDomainTablesAsync-&GT; SyncTableAsync-&GT; SaveObjects-&GT; SaveTable(类型)

我遇到的问题是在保存表中。如果我只是同步使用SaveTable我没有问题。在上面的异步方法中使用它会导致线程中止异常。 SQLite.net附带的SQLite.cs文件中抛出了异常(在。之内。奇怪的是,即使抛出异常,表也是在数据库中创建的。当Prepare()时会抛出一些错误函数被调用,其余时间调用SQLite3.Step()函数。

public int ExecuteNonQuery ()
{
    if (_conn.Trace) {
        Debug.WriteLine ("Executing: " + this);
    }

    var r = SQLite3.Result.OK;
    var stmt = Prepare (); // THROWS THE ERRROR
        r = SQLite3.Step(stmt); // THROWS THE ERRROR
    Finalize(stmt);
    if (r == SQLite3.Result.Done) {
        int rowsAffected = SQLite3.Changes (_conn.Handle);
        return rowsAffected;
    } else if (r == SQLite3.Result.Error) {
        string msg = SQLite3.GetErrmsg (_conn.Handle);
        throw SQLiteException.New (r, msg);
    } else {
        throw SQLiteException.New (r, r.ToString ());
    }
}

我假设因为我的foreach语句等待SyncTableAsync的返回,没有任何线程被关闭。我还收到一条system transaction critical exception,上面写着“试图访问已卸载的应用域”。

我是否对Sqlite3使用了await / async,或者这是我不知道的Sqlite3的问题。

附件是Parallel的堆栈和异常的照片。

enter image description here

编辑

当我尝试在单元测试中运行上面的代码时,单元测试过程永远不会死亡。我必须退出Visual Studio才能让进程死掉。我假设SQLite.dll中的某些东西在抛出异常并且不放弃时抓住了进程,但我不确定。

编辑2

我可以将初始方法SyncDomainTablesAsync修改为以下内容,代码运行时没有错误。问题是我使用异步并等待我相信。

    public async Task<int> SyncDomainTablesAsync(IProgress<string> progress, CancellationToken ct, DateTime? lastDateSynced=null, string tableName = null)
    {
        var domainTable = await GetDomainTablesAsync(progress,ct,lastDateSynced, tableName);
        var items = domainTable.Items;

        foreach (var item in items)
        {
            _coordinator.SaveObjects(item.Value, typeof(object), true);
        }
        return 1;
    }

0 个答案:

没有答案