gpt4 book ai didi

c# - 无法从未打开的数据库创建命令

转载 作者:IT王子 更新时间:2023-10-29 06:19:37 26 4
gpt4 key购买 nike

我已经搜索了很多,但找不到任何答案。

我正在编写一个 Xamarin Forms Mobile 应用程序,当我最小化该应用程序然后重新打开它或启动我的一个事件时,似乎会引发以下异常:

SQLiteConnection.CreateCommand (System.String cmdText, System.Object[] ps)
SQLite.SQLiteException: Cannot create commands from unopened database
SQLiteConnection.CreateCommand (System.String cmdText, System.Object[] ps)
TableQuery`1[T].GenerateCommand (System.String selectionList)
TableQuery`1[T].GetEnumerator ()
System.Collections.Generic.List`1[T]..ctor (System.Collections.Generic.IEnumerable`1[T] collection) [0x00062] in :0
Enumerable.ToList[TSource] (System.Collections.Generic.IEnumerable`1[T] source)
AsyncTableQuery`1[T].<ToListAsync>b__9_0 ()
Task`1[TResult].InnerInvoke ()
Task.Execute ()

这是我的代码:

Generic Repository(创建 Sqlite 实例的地方)

public class Repository<T> : IRepository<T> where T : Entity, new()
{
private readonly SQLiteAsyncConnection _db;

public Repository(string dbPath)
{
_db = new SQLiteAsyncConnection(dbPath);
_db.CreateTableAsync<T>().Wait();
}
}

国际奥委会注册

FreshIOC.Container.Register<IRepository<Settings>>(new Repository<Settings>(dbPath)); // FreshIOC is a wrapper around TinyIOC

在我的 App.xaml.cs OnResume 中

protected override void OnResume()
{
SQLiteAsyncConnection.ResetPool();
}

上面是ResetPool我把它放进去看看它是否会有所作为,但它没有。

URL 事件

protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);

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 = repo.Get().Result;
foreach (var s in settings)
{
var i = repo.Delete(s).Result;
}
repo.Save(new Settings
{
AccessToken = split[1],
OmbiUrl = split[0]
});
}

Intent startup = new Intent(this, typeof(MainActivity));
StartActivity(startup);
Finish();
}

我不确定还能做什么或寻找什么,我似乎找不到关于此类错误的任何信息。

更新:

经过更多调试后,它似乎只发生在 Url 事件完成后。我已经从 Activity 中删除了数据库代码,但它似乎仍然会发生。一旦 Activity 启动了主要的 App()然后运行这段代码:

var repo = FreshIOC.Container.Resolve<IRepository<Settings>>();
try
{
Task.Run(async () =>
{
settings = (await repo.Get()).FirstOrDefault();
}).Wait();
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
throw;
}

这是错误发生的地方。当 Get()被称为调用 return _db.Table<T>().ToListAsync();

我试过让所有东西都异步(没有帮助),建立存储库、连接和我们做的地方CreateTableAsync异步,仍然没有运气。

最佳答案

您正在进行同步阻塞调用,例如 .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,但存在同步调用。

同样,这可能会导致死锁,因此不建议这样做。

如果打算拥有响应式 UI 或使用 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);

关于c# - 无法从未打开的数据库创建命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50503394/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com