【问题标题】:Xamarin - Entity Framework - SqLite - GetItemsAsync : unable to open database fileXamarin - 实体框架 - SqLite - GetItemsAsync:无法打开数据库文件
【发布时间】:2019-06-02 00:09:18
【问题描述】:

在 Visual Studio 2017 中,我创建了一个跨平台移动应用 (Xamarin.Forms)

  • NET 标准 2.0
  • Microsoft.Data.Sqlite.Core 2.2.0
  • Microsoft.EntityFrameworkCore 2.2.0
  • Microsoft.EntityFrameworkCore.Sqlite 2.2.0
  • Microsoft.EntityFrameworkCore.Tools 2.2.0

我在the advice of Mark Smith 之后使用实体框架进行了代码第一次迁移到 Sqlite。所有表均已成功创建。数据库已播种。

当我尝试获取 ItemsViewModel(请参见下面的代码)时,我在该行上得到以下异常(20 秒后) --> List item list1 = await db.Person.ToListAsync();

Microsoft.Data.Sqlite.SqliteException (0x80004005):SQLite 错误 14: '无法打开数据库文件'。在 Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db) 在 Microsoft.Data.Sqlite.SqliteConnection.Open() 在 System.Data.Common.DbConnection.OpenAsync(CancellationToken 取消令牌)

奇怪的是,我的测试方法 TestSqLiteDataStoreAsync(参见下面的代码)正确地返回了预期的项目列表而不会导致错误。

此外,获取错误消息需要 20 秒这一事实表明它能够找到数据库。当您有错误的数据库名称/路径时,错误会立即弹出。

您可以在下面找到相关代码。如果您还需要什么,请告诉我。 也欢迎任何有关如何继续调试的帮助。或者有没有人可以比较我的基本工作 UWP 代码示例?我添加了一个 solution on Github 使用所需的数据库调用“temp”。如果有人可能有时间尝试重现错误:) 谢谢。

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ItemsPage : ContentPage
{
    ItemsViewModel viewModel;

    public ItemsPage()
    {
        InitializeComponent();
        //1 code starts here
        BindingContext = viewModel = new ItemsViewModel();
    }

    async void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
    {
        var item = args.SelectedItem as Item;
        if (item == null)
            return;

        await Navigation.PushAsync(new ItemDetailPage(new ItemDetailViewModel(item)));

        ItemsListView.SelectedItem = null;
    }

    async void AddItem_Clicked(object sender, EventArgs e)
    {
        await Navigation.PushModalAsync(new NavigationPage(new NewItemPage()));
    }


    //adding 'async' did not solve the problem
    protected async override void OnAppearing()
    {
        base.OnAppearing();

          if (viewModel.Items.Count == 0)
            //2 code continues here 
            viewModel.LoadItemsCommand.Execute(null);
    }
}


public class ItemsViewModel : BaseViewModel
{
    public ObservableCollection<Item> Items { get; set; }
    public Command LoadItemsCommand { get; set; }

    public ItemsViewModel()
    {
        Title = "Browse";
        Items = new ObservableCollection<Item>();

        //3 code continues here 
        LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());

        MessagingCenter.Subscribe<NewItemPage, Item>(this, "AddItem", async (obj, item) =>
        {
            var newItem = item as Item;
            Items.Add(newItem);
            await DataStore.AddItemAsync(newItem);
        });

    }

    //My test method
    public async Task<List<Item>> TestSqLiteDataStoreAsync()
    {
        SqLiteDataStore SqLiteDataStore = new SqLiteDataStore();
         return await SqLiteDataStore.GetItemsAsync(false);
    }



    async Task ExecuteLoadItemsCommand()
    {
        if (IsBusy)
            return;

        IsBusy = true;

        try
        {
            Items.Clear();
            //4 code continues here 
            var items = await DataStore.GetItemsAsync(true);
            foreach (var item in items)
            {
                Items.Add(item);
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }
        finally
        {
            IsBusy = false;
        }
    }
}



public class SqLiteDataStore : IDataStore<Item>
{
    public string dbFolder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
    public string fileName = "TestSqLite.db";

    public SqLiteDataStore()  { }

    public async Task<List<Item>> GetItemsAsync(bool forceRefresh = false)
    {
        try
        {
            using (var db = new Models.MyContext(Path.Combine(dbFolder, fileName)))
            {
                List<Person> list1 = new List<Person>();
                List<Item> list2 = new List<Item>();

                //5 I get the error here
                list1 = await db.Person.ToListAsync();

                foreach (Person person in list1)
                {
                    Item item = new Item(person);
                    list2.Add(item);
                }
                return list2;
            }
        }
        catch (Exception)
        {
            throw;
        }
     }


    public async Task<bool> AddItemAsync(Item item)
    {
       throw new NotImplementedException();
    }

    public Task<bool> UpdateItemAsync(Item item)
    {
        throw new NotImplementedException();
    }

    public Task<bool> DeleteItemAsync(string id)
    {
        throw new NotImplementedException();
    }

    public Task<Item> GetItemAsync(string id)
    {
        throw new NotImplementedException();
    }
}

【问题讨论】:

  • Environment.SpecialFolder.Personal 替换为Environment.SpecialFolder.LocalApplicationData 看看是否有帮助。

标签: c# sqlite xamarin xamarin.forms entity-framework-core


【解决方案1】:

根据获取

“无法打开数据库文件”

错误,也许你将代码指向一个不存在的文件。

“找不到表”错误意味着它找到了数据库,但没有找到您要查找的表。另一方面,“无法打开数据库文件”意味着它甚至找不到数据库,甚至没有费心寻找表。当您收到“找不到表”时,您离它正常工作更近了。

【讨论】:

  • 感谢您调查我的问题。当我使用 Xunit 运行我的测试方法“TestSqLiteDataStoreAsync”时,我没有收到任何错误。路径是正确的,数据库存在,我什至得到了一个项目列表。当从“ItemsViewModel”类调用“GetItemsAsync”时,我得到了异常。
  • 锁定问题?无法打开也可能意味着 * 权限或 * 其他东西持有锁,因此无法打开文件。
  • @TomTom 确实可能是原因。我检查了文件属性并为每个人设置了完全权限:)。无济于事。从 XUnit 运行的测试方法没有问题。
【解决方案2】:

正如我们之前提到的,只需将Environment.SpecialFolder.Personal 替换为Environment.SpecialFolder.LocalApplicationData 即可解决问题。您可以查看Environment.SpecialFolder Enum 了解每个选项的详细信息。现在您的项目正在正确加载:


回答您的后续 cmets:

是的,您在 github 上发布的MCVE 现在运行没有任何问题按照我们描述的更改为LoadApplicationData 之后,包括您的测试代码,如下所示:

[3/10/2019 4:26:28 PM Informational] [xUnit.net 00:00:00.02] xUnit.net VSTest Adapter v2.4.0 (64-bit .NET Core 4.6.27129.04)
[3/10/2019 4:26:31 PM Informational] [xUnit.net 00:00:03.41]   Discovering: XUnitTestProject1
[3/10/2019 4:26:31 PM Informational] [xUnit.net 00:00:03.61]   Discovered:  XUnitTestProject1
[3/10/2019 4:26:31 PM Informational] ========== Discover test finished: 1 found (0:00:11.5651246) ==========
[3/10/2019 4:26:31 PM Informational] ------ Run test started ------
[3/10/2019 4:26:32 PM Informational] [xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.4.0 (64-bit .NET Core 4.6.27129.04)
[3/10/2019 4:26:33 PM Informational] [xUnit.net 00:00:00.63]   Discovering: XUnitTestProject1
[3/10/2019 4:26:33 PM Informational] [xUnit.net 00:00:00.70]   Discovered:  XUnitTestProject1
[3/10/2019 4:26:33 PM Informational] [xUnit.net 00:00:00.70]   Starting:    XUnitTestProject1
[3/10/2019 4:26:35 PM Informational] [xUnit.net 00:00:02.60]   Finished:    XUnitTestProject1
[3/10/2019 4:26:35 PM Informational] ========== Run test finished: 1 run (0:00:03.9952011) ==========

如果我们切换回Personal 文件夹,我们会得到与您报告的相同的异常:

Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 14: 'unable to open database file'.
   at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
   at Microsoft.Data.Sqlite.SqliteConnection.Open()
   at System.Data.Common.DbConnection.OpenAsync(CancellationToken cancellationToken)

您可以运行测试代码但不能运行应用程序的原因可能是因为这两个具有不同的权限要求。

请记住,要在这两种情况下都有效,您不仅要更改Environment.SpecialFolder,还要将TestSqLite.db 放在相应的文件夹中。在我们的例子中,文件夹是这样的:

C:\Users\SomeUser\AppData\Local\Packages\0eea1ae6-65c3-42e9-9a50-869730e52f63_2ft3g98yvwh70\LocalState

显然你应该得到一个相似但略有不同的路径。


关于结果验证的评论:执行这个简单的验证过程只需要几分钟。下面是当我们切换回Environment.SpecialFolder.Personal 时发生的情况,在其对应的C:\Users\SomeUser\Documents 文件夹中有TestSqLite.db

如下图所示,该应用加载初始浏览页面所需的时间比正常时间长(此处缩短以限制上传大小)。当它最终完成时,初始页面是空的。在 关于 页面之间切换,然后返回 Browse 页面会导致应用挂起(等待它)。同样,这是一个简单的过程,只需很少的时间和精力即可执行。

【讨论】:

  • 感谢您调查我的问题。更改检索数据库的路径并没有解决我的问题。在我的代码中,它指向正确的数据库(我的测试代码有效)。我看到您设法使用我的示例 sql lite 数据库运行我的代码(正确吗?)。这将帮助我找到我的错误。
  • @ywwy 请查看我们更新的答案。让我们知道进展如何。
  • @ywwy 请将我们的答案标记为已接受,这是正确的做法...我们的建议消除了您报告的异常,应用程序加载其项目并立即运行干净,测试代码也是如此,正如我们帖子中的大量证据所记录的那样。
  • 非常感谢您的帮助,您的回答可能是正确的,请给我一些时间确认。
  • @ywwy 请参阅我们更新答案的关于结果验证的评论部分。
猜你喜欢
  • 1970-01-01
  • 2014-11-11
  • 2014-08-30
  • 1970-01-01
  • 2021-10-15
  • 2015-08-03
  • 1970-01-01
  • 1970-01-01
  • 2021-02-15
相关资源
最近更新 更多