Ste*_*ald 5 c# sqlite xamarin.ios xamarin sqlite.net
我们正在使用以下内容:
该应用程序在后台线程上与服务器同步数据。整个应用程序只共享一个 SQLite 连接对象。前台查询在后台同步运行的同时执行。所有这些都在应用程序的 Windows 8.1 版本上运行良好(即,在 MSFT Surface 和类似版本上)。然而,一旦我们切换到 Xamarin/mono,我们就会开始不断崩溃,如下所示。
研究导致了这篇文章:http : //www.aaronheise.com/2012/12/monotouch-sqlite-sigsegv/
他使用的是 Mono.Data.SqliteClient,而不是我们现在使用的 sqlite.net。
他的解决方案涉及明确处理 Command 对象以确保 GC 可以跟上等。当我尝试将我的 Command 对象(来自 sqlite.net)包装在 using(){} 子句中时,我发现它们不是一次性的。
我试过插入 100 毫秒的延迟并阻止崩溃,但这对我们来说不是一个可行的解决方案。
这里对 sqlite.net 有什么希望,还是我应该寻找一种不同的方式来使用 sqlite?
mono-rt: Stacktrace:
mono-rt: at <unknown> <0xffffffff>
mono-rt: at (wrapper managed-to-native) SQLite.SQLite3.Prepare2 (intptr,string,int,intptr&,intptr) <IL 0x0003c, 0xffffffff>
...
mono-rt:
Native stacktrace:
mono-rt:
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
Run Code Online (Sandbox Code Playgroud)
我很确定当我尝试从多个线程敲击相同的 sqlite.net 连接时,我得到了有意义的错误而不是 SIGSEGV 的错误,但如果您认为这是罪魁祸首,那么解决方案很简单:您需要限制对任何 sqlite 的访问。一次接触数据库到一个线程的 .net 方法。
在您在应用程序中共享单个实例的情况下SQLiteConnection(这是一种完全有效的做事方式),我建议创建一个简化的代理类来包装您的 sqlite.net 连接,仅公开您想要的方法并保护访问对于那些有lock陈述的人,即:
public class DatabaseWrapper : IDisposable
{
// Fields.
private readonly SQLiteConnection Connection;
private readonly object Lock = new object();
public DatabaseWrapper(string databasePath)
{
if (string.IsNullOrEmpty(databasePath)) throw new ArgumentException("Database path cannot be null or empty.");
this.Connection = new SQLiteConnection(databasePath);
}
public IEnumerable<T> Entities<T>() where T : new()
{
lock (this.Lock)
{
return this.Connection.Table<T>();
}
}
public IEnumerable<T> Query<T>(string query, params object[] args) where T : new()
{
lock (this.Lock)
{
return this.Connection.Query<T>(query, args);
}
}
public int ExecuteNonQuery(string sql, params object[] args)
{
lock (this.Lock)
{
return this.Connection.Execute(sql, args);
}
}
public T ExecuteScalar<T>(string sql, params object[] args)
{
lock (this.Lock)
{
return this.Connection.ExecuteScalar<T>(sql, args);
}
}
public void Insert<T>(T entity)
{
lock (this.Lock)
{
this.Connection.Insert(entity);
}
}
public void Update<T>(T entity)
{
lock (this.Lock)
{
this.Connection.Update(entity);
}
}
public void Upsert<T>(T entity)
{
lock (this.Lock)
{
var rowCount = this.Connection.Update(entity);
if (rowCount == 0)
{
this.Connection.Insert(entity);
}
}
}
public void Delete<T>(T entity)
{
lock (this.Lock)
{
this.Connection.Delete(entity);
}
}
public void Dispose()
{
this.Connection.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
PS 显然,由于您在多个线程上执行操作,因此您需要非常小心,不要引入竞争条件,这就是为什么,例如,我包含了Upsert保证以原子方式执行两步“更新或插入”操作的方法。