您正在进行同步阻塞调用,例如
.Wait()
和
.Result
当与异步API混合时,这可能会导致死锁。
SQLiteAsyncConnection
是为了异步使用。
一个常见的解决方法是创建允许进行异步非阻塞调用的事件处理程序。
例如,当调用
CreateTableAsync
在存储库中
public class Repository<T> : IRepository<T> where T : Entity, new() {
private readonly SQLiteAsyncConnection _db;
public Repository(string dbPath) {
_db = new SQLiteAsyncConnection(dbPath);
createTable += onCreateTable; //Subscribe to event
createTable(this, EventArgs.Empty); //Raise event
}
private event EventHandler createTable = delegate { };
private async void onCreateTable(object sender, EventArgs args) {
createTable -= onCreateTable; //Unsubscribe from event
await _db.CreateTableAsync<T>(); //async non blocking call
}
//...
}
存储库抽象似乎有一个异步API,但仍有同步调用。
同样,这会导致僵局,不建议这样做。
如果想要有一个响应性的用户界面或使用,则需要对代码进行重构,以便一直保持异步状态。
sqlite.net网站
非异步版本,用于进行同步调用。
将URL活动重构为异步的方式与上面相同。
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
creating += onCreateCore; //subscribe to event
creating(this, EventArgs.Empty); //raise event
}
private event EventHandler creating = delegate { };
private async void onCreateCore(object sender, EventArgs args) {
creating -= onCreateCore; //unsubscribe to event
var url = Intent.Data.ToString();
var split = url.Split(new[] { "ombi://", "_" }, StringSplitOptions.RemoveEmptyEntries);
if (split.Length > 1) {
var dbLocation = new FileHelper().GetLocalFilePath("ombi.db3");
var repo = new Repository<OmbiMobile.Models.Entities.Settings>(dbLocation);
var settings = await repo.Get();
foreach (var s in settings) {
var i = await repo.Delete(s);
}
repo.Save(new Settings {
AccessToken = split[1],
OmbiUrl = split[0]
});
}
Intent startup = new Intent(this, typeof(MainActivity));
StartActivity(startup);
Finish();
}
更新
同样,从设计的角度来看,连接的初始化应该从存储库中反转出来,并从外部进行管理(SRP)。
public interface ISQLiteAsyncProvider {
SQLiteAsyncConnection GetConnection();
}
public class DefaultSQLiteAsyncProvider : ISQLiteAsyncProvider {
private readonly Lazy<SQLiteAsyncConnection> connection;
public DefaultSQLiteAsyncProvider(string path) {
connection = new Lazy<SQLiteAsyncConnection>(() => new SQLiteAsyncConnection(path));
}
public SQLiteAsyncConnection GetConnection() {
return connection.Value;
}
}
利用异步延迟初始化连接的思想,使用
/// <summary>
/// Provides support for asynchronous lazy initialization.
/// </summary>
/// <typeparam name="T"></typeparam>
public class LazyAsync<T> : Lazy<Task<T>> {
/// <summary>
/// Initializes a new instance of the LazyAsync`1 class. When lazy initialization
/// occurs, the specified initialization function is used.
/// </summary>
/// <param name="valueFactory">The delegate that is invoked to produce the lazily initialized Task when it is needed.</param>
public LazyAsync(Func<Task<T>> valueFactory) :
base(() => Task.Run(valueFactory)) { }
}
这使得现在可以重构存储库以使用惰性初始化,这允许在存储库中删除事件处理程序。
public class Repository<T> : IRepository<T> where T : Entity, new() {
public Repository(ISQLiteAsyncProvider provider) {
this.connection = new LazyAsync<SQLiteAsyncConnection>(await () => {
var db = provider.GetConnection();
await db.CreateTableAsync<T>();
return db;
});
}
private readonly LazyAsync<SQLiteAsyncConnection> connection;
public async Task<List<T>> Get() {
var _db = await connection.Value;
return await _db.Table<T>().ToListAsync();
}
public async Task<T> Get(int id) {
var _db = await connection.Value;
return await _db.Table<T>().Where(x => x.Id == id).FirstOrDefaultAsync();
}
public async Task<int> Save(T entity) {
var _db = await connection.Value;
return entity.Id == 0
? await _db.InsertAsync(entity)
: await_db.UpdateAsync(entity);
}
public async Task<int> Delete(T entity) {
var _db = await connection.Value;
return await _db.DeleteAsync(entity);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
// get rid of managed resources
}
// get rid of unmanaged resources
}
}
注册的方式
// same instance should be used for other repositories
var provider = new DefaultSQLiteAsyncProvider(dbPath);
var settingsRepository = new Repository<Settings>(provider);
FreshIOC.Container.Register<IRepository<Settings>>(settingsRepository);