Xamarin - Entity Framework - SqLite - GetItemsAsync: невозможно открыть файл базы данных - PullRequest
0 голосов
/ 07 января 2019

В Visual studio 2017 я создал кроссплатформенное мобильное приложение (Xamarin.Forms)

  • NETStandard 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

Сначала я выполнил миграцию кода на Sqlite с Entity Framework, следуя совету Марка Смита . Все таблицы созданы успешно. База данных была заполнена.

Когда я пытаюсь получить ItemsViewModel (см. Код ниже), я получаю следующее исключение (через 20 секунд) в строке -> Список элементов 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 CancellationToken)

Как ни странно, мой тестовый метод TestSqLiteDataStoreAsync (см. Код ниже) правильно возвращает ожидаемый список элементов, не вызывая ошибки.

Кроме того, тот факт, что для получения сообщения об ошибке требуется 20 секунд, указывает на то, что он смог найти базу данных. Если у вас неверное имя / путь в БД, ошибка появляется сразу.

Ниже вы найдете соответствующий код. Если вам нужно что-то еще, дайте мне знать. Любая помощь по продолжению отладки также приветствуется. Или у кого-нибудь есть базовый пример работающего кода UWP, с которым я могу сравнить мой? Я добавил решение на Github называется temp с необходимой базой данных. Если бы у вас было время попытаться воспроизвести ошибку :) Thx.

[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();
    }
}

Ответы [ 2 ]

0 голосов
/ 09 марта 2019

Как мы уже упоминали ранее, простая замена Environment.SpecialFolder.Personal на Environment.SpecialFolder.LocalApplicationData решит проблему. Вы можете проверить Environment.SpecialFolder Enum для получения подробной информации о каждой опции. И теперь ваши вещи загружаются правильно:

enter image description here


Ответ на ваши последующие комментарии:

Да, MCVE , который вы разместили на github, теперь работает без проблем после , изменив значение на 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) ==========

enter image description here

Если мы вернемся к папке 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, имея TestSqLite.db в соответствующей папке C:\Users\SomeUser\Documents.

Как показано ниже, для загрузки начальной страницы Browse приложению требуется больше времени, чем обычно (здесь сокращено для ограничения размера загрузки). Когда это наконец произойдет, начальная страница будет пустой. Переключение между страницей About и затем обратно на Просмотр страницы приводит к зависанию приложения (дождитесь его). Опять же, это простая процедура, которая занимает мало времени и не требует усилий.

enter image description here

0 голосов
/ 08 января 2019

По данным получения

"Невозможно открыть файл базы данных"

ошибка, возможно, вы указываете код на файл, который не существует.

Ошибка «Таблица не найдена» означает, что она нашла базу данных, но не ту таблицу, которую вы искали. С другой стороны, «Невозможно открыть файл базы данных» означает, что он даже не смог найти базу данных и даже не стал искать таблицу. Вы намного ближе к правильной работе, когда вы получаете «Таблица не найдена».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...