Where to put and how to read from manually created DB file using Xamarin forms?

99封情书 提交于 2020-03-25 19:19:11

问题


I created questions.db file using DB Browser.

Here it is: My lovely .db file

I want to add it to Xamarin.Forms solution and read data from it. (This is going to be quiz app)

That sounds pretty simple, right, but I'm stuck googling for hours. Most answers just link to this https://docs.microsoft.com/en-us/xamarin/xamarin-forms/data-cloud/data/databases

Which explains nothing about the manually created database file, where to put it etc.

EDIT: this is what I'm trying to achieve: https://arteksoftware.com/deploying-a-database-file-with-a-xamarin-forms-app/

Sadly, the guide is outdated and many things of it doesn't work.

So where do I put my .db file? In MyApp.Android Assets and Resources for MyApp.iOS? If so, how do I get DBpath string then for new connection?

_database = new SQLiteAsyncConnection(dbPath);

回答1:


If what you want to do is:

Add a pre existing database to your project and load that database to be used by your App then keep on reading.


Short Answer

You need to add your database file to a specific folder in the paltform project (Assets in Android, in iOS you can simply create a folder and put the file there) and use DependencyService to access it. Copy the database file to somewhere in your device (App data folder or InternalStorage or watever is allowed on each platform) and use that final path in the SQLite constructor. From now on, you can simply use that SQLite connection as any other to perform CRUD operation on your Database.


Long Answer

I will now describe how to achieve this step by step from creating a new Xamarin.Forms project to reading data from the preloaded database.

Note: the follwoing solution will only cover the Android part of the CrossPlatform solution. Extending this to iOS should be no problem.

Disclaimer: The code presented next should just be taken as a guideline and by no means I suggest that is proved or production like.


0. Getting project ready:

Create a new Xamarin.Forms project (on my case i use Visual Studio v. 16.3.8 and the new project has Xamarin.Forms 4.2 installed)

Then, first of all, install sqlite nuget package in ALL your projects:


1. Prepare your Database

In App.xaml.cs file

// Create db static property to perform the 
// Database calls from around the project
public static Database db { get; set; }

public App()
{
    InitializeComponent();


    MainPage = new MainPage();
}

Create a new class file and name it Database.cs. There you open the database connection.

public class Database
{
    // Create a SQLiteAsyncConnection property to be accessed 
    // publicly thru your App.
    public SQLiteAsyncConnection DBInstance { get; set; }

    public Database(String databasePath)
    {
        DBInstance = new SQLiteAsyncConnection(databasePath);

    }


    private async Task<string> GetDatabaseFilePath()
    {
        return await DependencyService.Get<IPathFinder>().GetDBPath();
    }
}

So far so good, but... What should actually be the path of your preloaded database? Well, that is the next part.


2. Load pre-existent database to your project and get a path to it (Android)

So the big question is where to store your pre-existent database in the project.

One option is to add it as an Asset. To implement this approach, do the following:

2.1 Add database file to Assets folder in Android project

Now we want to access that database. To do so, follow the next steps:

2.2 Create an Interface IPathFinder in your App project

And there define a single member GetDBPath()

public interface IPathFinder
{

    Task<String> GetDBPath();

}

Now, in your Android project create a class file PathFinder.cs to implement this interface

and implement the method (Note the Dependency Attribute above the namespace!)

using System;
using SysIO = System.IO;
using System.Threading.Tasks;

using Java.IO;

using Xamarin.Forms;

using TestDB.Droid;

[assembly: Dependency(typeof(PathFinder))]
namespace TestDB.Droid
{
    public class PathFinder : IPathFinder
    {
        public async Task<string> GetDBPath()
        {

            String dbPath = String.Empty;

            if (await PermissonManager.GetPermission(PermissonManager.PermissionsIdentifier.Storage))
            {

                String systemPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.RootDirectory.Path).Path;

                String tempFolderPath = SysIO::Path.Combine(systemPath, "MytestDBFolder");
                if (!SysIO::File.Exists(tempFolderPath))
                {
                    new File(tempFolderPath).Mkdirs();
                }

                dbPath = SysIO::Path.Combine(tempFolderPath, "test.db");

                if (!SysIO::File.Exists(dbPath))
                {
                    Byte[] dbArray;
                    using (var memoryStream = new SysIO::MemoryStream())
                    {
                        var dbAsset = MainActivity.assets.Open("test.db");
                        dbAsset.CopyTo(memoryStream);
                        dbArray = memoryStream.ToArray();
                    }    

                    SysIO.File.WriteAllBytes(dbPath, dbArray);

                }    

            }    

            return dbPath;
        }
    }
}

In the GetDBPath() method, the first to note is the GetPermission method. This is needed since Android API 23 in order to manage the App permissions.

Create a file called PermissonManager in your Android project

And add the code below

using System;
using System.Threading.Tasks;
using Android;
using Android.App;
using Android.Content.PM;
using Android.OS;
using Android.Support.V4.App;

namespace TestDB.Droid
{
    public class PermissonManager
    {

        public enum PermissionsIdentifier
        {
            Storage // Here you can add more identifiers.
        }

        private static String[] GetPermissionsRequired(PermissionsIdentifier identifier)
        {

            String[] permissions = null;


            if (identifier == PermissionsIdentifier.Storage)

                permissions = PermissionExternalStorage;


            return permissions;

        }

        private static Int32 GetRequestId(PermissionsIdentifier identifier)
        {

            Int32 requestId = -1;


            if (identifier == PermissionsIdentifier.Storage)

                requestId = ExternalStorageRequestId;


            return requestId;

        }


        public static TaskCompletionSource<Boolean> PermissionTCS;


        public static readonly String[] PermissionExternalStorage = new String[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage };

        public const Int32 ExternalStorageRequestId = 2;



        public static async Task<Boolean> GetPermission(PermissionsIdentifier identifier)
        {

            Boolean isPermitted = false;

            if ((Int32)Build.VERSION.SdkInt < 23)

                isPermitted = true;

            else

                isPermitted = await GetPermissionOnSdk23OrAbove(GetPermissionsRequired(identifier), GetRequestId(identifier));


            return isPermitted;

        }



        private static Task<Boolean> GetPermissionOnSdk23OrAbove(String[] permissions, Int32 requestId)
        {

            PermissionTCS = new TaskCompletionSource<Boolean>();

            if (MainApplication.CurrentContext.CheckSelfPermission(permissions[0]) == (Int32)Permission.Granted)

                PermissionTCS.SetResult(true);

            else

                ActivityCompat.RequestPermissions((Activity)MainApplication.CurrentContext, permissions, requestId);


            return PermissionTCS.Task;

        }


        public static void OnRequestPermissionsResult(Permission[] grantResults)
        {

            PermissionTCS.SetResult(grantResults[0] == Permission.Granted);

        }

    }
}

In that class you note the presence of MainApplication, which provides the CurrentContext. You will also have to add that class file

and there add the following code

using System;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;


namespace DemoDB.Droid
{
    [Application]
    public partial class MainApplication : Application, Application.IActivityLifecycleCallbacks
    {

        private static Context _currentContext = Application.Context;
        internal static Context CurrentContext
        {
            get => _currentContext;
            private set
            {
                _currentContext = value;
            }
        }


        internal static String FileProviderAuthority
        {
            get => MainApplication.CurrentContext.ApplicationContext.PackageName + ".fileprovider";
        }


        public MainApplication(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
        {

        }


        public override void OnCreate()
        {

            base.OnCreate();

            RegisterActivityLifecycleCallbacks(this);

        }


        public override void OnTerminate()
        {

            base.OnTerminate();

            UnregisterActivityLifecycleCallbacks(this);

        }


        public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
        {

            CurrentContext = activity;

        }


        public void OnActivityDestroyed(Activity activity)
        {

        }


        public void OnActivityPaused(Activity activity)
        {

        }


        public void OnActivityResumed(Activity activity)
        {

            CurrentContext = activity;

        }


        public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
        {

        }


        public void OnActivityStarted(Activity activity)
        {

            CurrentContext = activity;

        }


        public void OnActivityStopped(Activity activity)
        {

        }    
    }
}

Then your PermissonManager is almost ready. Now you just have to override OnRequestPermissionsResult in the MainActivity file

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
    PermissonManager.OnRequestPermissionsResult(grantResults);
    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}

Getting back to the GetPath() method, you see a mysterious MainActivity.assets property call. This has to be created in the MainActivity as follows

public static AssetManager assets;

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

    assets = this.Assets;

    base.OnCreate(savedInstanceState);

    Xamarin.Essentials.Platform.Init(this, savedInstanceState);
    global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
    LoadApplication(new App());
}

Almost ready! Now you just call your database.

3. Use your database!

From the OnAppearing of the Main page, make a simple call to the database, to create it and access it.

protected override async void OnAppearing()
{
    String databasePath = await Database.GetDatabaseFilePath();

    App.db = new Database(databasePath);

    var table = await App.db.DBInstance.CreateTableAsync<Category>();

    // here Category is a class that models the objects 
    // present in my pre-existent database
    List<Category> categories = new List<Category>();

    categories = await App.db.DBInstance.Table<Category>().ToListAsync();

    base.OnAppearing();
}

And that is it.

I hope this is helpful :P



来源:https://stackoverflow.com/questions/58801549/where-to-put-and-how-to-read-from-manually-created-db-file-using-xamarin-forms

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!