【问题标题】:Cannot read SQLite file in Xamarin Android project无法读取 Xamarin Android 项目中的 SQLite 文件
【发布时间】:2020-03-08 15:40:26
【问题描述】:

我有一个名为Lego_Parts.db3 的文件,它是一个已经填充了数据的 SQLite 文件。我在 Android Xamarin 项目的资产文件夹中有它。我必须设置数据库路径的代码是:

static PieceDB database;

public static PieceDB PieceDatabase
{
        get
        {
            if (database == null)
            {
                database = new PieceDB(Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "Lego_Parts.db3"));
            }
            return database;
        }
}

当我尝试显示数据库中的数据(在 DatabaseTest.xaml 中)时,没有数据显示

这是任何适用的代码:

Piece.cs

using SQLite;

namespace TestApp1
{
    public class Piece
    {
        public int PartNum { get; set; }
        public string PartName { get; set; }
        public string Category { get; set; }
        public string Url { get; set; }
    }
}

PieceDB.cs

using SQLite;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace TestApp1
{
    public class PieceDB
    {
        readonly SQLiteAsyncConnection _database;

        public PieceDB(string dbPath)
        {
            _database = new SQLiteAsyncConnection(dbPath);
            _database.CreateTableAsync<Piece>().Wait();
        }

        public Task<List<Piece>> GetAllPieces()
        {
            return _database.Table<Piece>().ToListAsync();
        }

        public Task<Piece> GetPiece(int partNum)
        {
            return _database.Table<Piece>().Where(i => i.PartNum == partNum).FirstOrDefaultAsync();
        }

        public Task<int> SavePieceAsync(Piece temp)
        {
            return _database.InsertAsync(temp);
        }
    }
}

DatabaseTest.xaml.cs


using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace TestApp1
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class DatabaseTest : ContentPage
    {

        protected override async void OnAppearing()
        {
            base.OnAppearing();

            listView.ItemsSource = await App.PieceDatabase.GetAllPieces();
        }

        public DatabaseTest()
        {
            InitializeComponent();
        }

        async void Handle_ItemTapped(object sender, ItemTappedEventArgs e)
        {
            if (e.Item == null)
                return;

            await DisplayAlert("Item Tapped", "An item was tapped.", "OK");

            //Deselect Item
            ((ListView)sender).SelectedItem = null;
        }
    }
}

DatabaseTest.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="TestApp1.DatabaseTest">
    <StackLayout Margin="20,35,20,20">
        <ListView x:Name="listView">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding PartNum}"
                              Detail="{Binding Url}"
                              />
                </DataTemplate>
            </ListView.ItemTemplate>

        </ListView>
    </StackLayout>
</ContentPage>

【问题讨论】:

标签: c# sqlite xamarin xamarin.android


【解决方案1】:

您必须将数据库复制到文件位置。

首先,我建议将您的 db3 文件放在 Resources/Raw 文件夹中,因为它会使复制稍微容易一些。此外,Android 资源只能使用小写字母、数字和下划线,并且必须以字母开头,因此首先将您的 db 文件名更改为 lego_parts.db3。

然后在 MainActivity 的 OnCreate 中,执行以下操作:

var dbPath = Path.Combine (System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal), "lego_parts.db3"); // FILE NAME TO USE WHEN COPIED
var s = Resources.OpenRawResource(Resource.Raw.lego_parts);  // DATA FILE RESOURCE ID
if (!System.IO.File.Exists(dbPath)) {
    FileStream writeStream = new FileStream(dbPath, FileMode.OpenOrCreate, FileAccess.Write);
    ReadWriteStream(s, writeStream);
}

并将以下方法添加到 MainActivity 类:

private void ReadWriteStream(Stream readStream, Stream writeStream)
{
    int Length = 256;
    Byte[] buffer = new Byte[Length];
    int bytesRead = readStream.Read(buffer, 0, Length);
    // write the required bytes
    while (bytesRead > 0)
    {
        writeStream.Write(buffer, 0, bytesRead);
        bytesRead = readStream.Read(buffer, 0, Length);
    }
    readStream.Close();
    writeStream.Close();
}

然后您可以使用 dbPath 作为文件路径连接到您的数据库。

在 iOS 上,你可以将 lego_parts.db3 放在 iOS 项目的根文件夹中,然后在 AppDelegate.FinishedLaunching 方法中使用以下代码进行复制:

var dbPath = Path.Combine (System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal), "lego_parts.db3");
var appDir = NSBundle.MainBundle.ResourcePath;
var seedFile = Path.Combine(appDir, "lego_parts.db3");
if (!File.Exists(dbPath) && File.Exists(seedFile))
    File.Copy(seedFile, dbPath);

【讨论】:

  • 感谢您的帮助,但是我在尝试构建时遇到此错误:错误 CS0117:“资源”不包含“原始”的定义
  • 您是否将 Raw 文件夹添加到 Resources 文件夹并将您的 lego_parts.db3 文件添加到 Raw 文件夹?那是我说的第一件事,“首先,我建议将您的 db3 文件放在 Resources/Raw 文件夹中……”另外,我意识到我遗漏了 ReadWriteStream 方法……将其添加到我的答案中。
  • 是的,我添加了 Raw 文件夹,重命名了 db 文件并将其放在 raw 文件夹中。仍然给我这个错误,尽管我认为它是 IDE 而不是代码的问题
【解决方案2】:

通过在 android 项目中创建此类,我能够从 android assets 文件夹中复制数据库文件:

CreateConnection.cs

namespace TestApp1.Droid
{
    public class CreateConnection
    {
        public void Open()
        {
            var sqliteFilename = "lego_parts.db3";
            string documentsDirectoryPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
            var path = Path.Combine(documentsDirectoryPath, sqliteFilename);

            if(!File.Exists(path))
            {
                using (var binaryReader = new BinaryReader(Android.App.Application.Context.Assets.Open(sqliteFilename)))
                {
                    using (var binaryWriter = new BinaryWriter(new FileStream(path, FileMode.Create)))
                    {
                        byte[] buffer = new byte[2048];
                        int length = 0;
                        while((length = binaryReader.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            binaryWriter.Write(buffer, 0, length);
                        }
                    }
                }
            } else
            {
                Console.WriteLine("Database already saved on device");
            }


        }
    }
}

并从 Android 项目的 OnCreate() 方法中调用 Open() 方法

MainActivity.cs

protected override async void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);

            await CrossMedia.Current.Initialize(); 

            CrossCurrentActivity.Current.Init(this, savedInstanceState);
            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            CreateConnection connection = new CreateConnection();
            connection.Open();
            LoadApplication(new App());
        }

【讨论】:

    猜你喜欢
    • 2017-07-19
    • 1970-01-01
    • 2016-10-19
    • 1970-01-01
    • 2016-08-29
    • 1970-01-01
    • 1970-01-01
    • 2017-03-21
    • 1970-01-01
    相关资源
    最近更新 更多