Настройка базы данных (SQLite) для Unity - PullRequest
0 голосов
/ 08 июня 2018

Я просмотрел слишком много учебников, чтобы перечислить их, и все они рекомендуют одно и то же.Однако они не помогли решить мою проблему.

Я пытаюсь включить в свой проект БД SQLite, а при сборке для ПК, MAC и Linux Standalone (тестирование на компьютере с Windows) база данных работаеткак и ожидалось.При тестировании на устройстве Android я получаю следующие ошибки.

   E/Unity: ArgumentException: Invalid ConnectionString format for parameter "/storage/emulated/0/Android/data/com.tbltools.tbl_project/files/TBLDatabase.db"
          at Mono.Data.Sqlite.SqliteConnection.ParseConnectionString (System.String connectionString) [0x00000] in <filename unknown>:0 
          at Mono.Data.Sqlite.SqliteConnection.Open () [0x00000] in <filename unknown>:0 
          at UIHandler+<RequestAllStudentNames>c__Iterator2.MoveNext () [0x00000] in <filename unknown>:0 
          at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0 

Я думал, что внесение поправки в строку подключения должно быть достаточно простым, но это не решило мою проблему.Это то, что у меня пока есть:

   if (Application.platform != RuntimePlatform.Android)
        {
            // The name of the db.
             tblDatabase = "URI=file:" + Application.dataPath + "/TBLDatabase.db"; //returns the complete path to database file exist.
        }
        else
        {
              tblDatabase = Application.persistentDataPath + "/TBLDatabase.db";

            if (!File.Exists(tblDatabase))
            {
                // if it doesn't ->
                Debug.LogWarning("File \"" + tblDatabase + "\" does not exist. Attempting to create from \"" + Application.dataPath + "!/assets/" + "TBLDatabase.db");
                // open StreamingAssets directory and load the db ->

                // #if UNITY_ANDROID
                var loadDb = new WWW("jar:file://" + Application.dataPath + "!/assets/" + "TBLDatabase.db");  // this is the path to your StreamingAssets in android
                while (!loadDb.isDone) { }  // CAREFUL here, for safety reasons you shouldn't let this while loop unattended, place a timer and error check
                                            // then save to Application.persistentDataPath
                File.WriteAllBytes(tblDatabase, loadDb.bytes);
            }
        }
        //open db connection
        var connection = new SqliteConnection(tblDatabase);
        connection.Open();
        var command = connection.CreateCommand();

Я использовал оболочку adb и вытащил БД из моего устройства Android, и все как и ожидалось (БД существует, и она не пуста).

Я считаю, что у меня есть все соответствующие файлы DLL, но если кто-нибудь может дать мне некоторые рекомендации, я был бы признателен.

****************************************************РЕДАКТИРОВАТЬ**********************************************

С тех пор я произвел следующие изменения, основываясь на данном совете.

Сейчас я вызываю следующий метод для запуска моего соединения и обработки запросов к БД StartCoroutine(RunDbCode(dbFileName, jsonStudentID, jsonIndiNames, jsonIndiStudentNumbers));

Тогда у меня есть следующий метод:

IEnumerator RunDbCode(string fileName, List jsonStudentID, List jsonIndiNames, List jsonIndiStudentNumbers)
    {
        //Where to copy the db to
        string dbDestination = Path.Combine(Application.persistentDataPath, "data");
        dbDestination = Path.Combine(dbDestination, fileName);

        //Check if the File do not exist then copy it
        if (!File.Exists(dbDestination))
        {
            //Where the db file is at
            string dbStreamingAsset = Path.Combine(Application.streamingAssetsPath, fileName);

            byte[] result;

            //Read the File from streamingAssets. Use WWW for Android
            if (dbStreamingAsset.Contains("://") || dbStreamingAsset.Contains(":///"))
            {
                WWW www = new WWW(dbStreamingAsset);
                yield return www;
                result = www.bytes;
            }
            else
            {
                result = File.ReadAllBytes(dbStreamingAsset);
            }
            Debug.Log("Loaded db file");

            //Create Directory if it does not exist
            if (!Directory.Exists(Path.GetDirectoryName(dbDestination)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(dbDestination));
            }

            //Copy the data to the persistentDataPath where the database API can freely access the file
            File.WriteAllBytes(dbDestination, result);
            Debug.Log("Copied db file");
        }

        //Now you can do the database operation
        //open db connection
        var connection = new SqliteConnection(dbDestination);
        connection.Open();
        var command = connection.CreateCommand();

        // Drop the table if it already exists.
        command.CommandText = "DROP TABLE IF EXISTS existing_individual;";
        command.ExecuteNonQuery();

        var sql = "CREATE TABLE existing_individual (studentID VARCHAR(23), fullName VARCHAR(50), studentNumber VARCHAR(20))";
        command.CommandText = sql;
        command.ExecuteNonQuery();

        //Inserting the exisiting student names returned, into the SQLite DB 

        int count = 0;

        foreach (var individuals in jsonStudentID)
        {
            //looping through the existing students registered for the individual quiz - below has been written to avoid SQL injection
            sql = "INSERT INTO existing_individual (studentID, fullName, studentNumber) VALUES (@jsonStudentID, @jsonIndiNames, @jsonIndiStudentNumbers)";
            command.Parameters.AddWithValue("@jsonStudentID", jsonStudentID[count]);
            command.Parameters.AddWithValue("@jsonIndiNames", jsonIndiNames[count]);
            command.Parameters.AddWithValue("@jsonIndiStudentNumbers", jsonIndiStudentNumbers[count]);

            command.CommandText = sql;
            command.ExecuteNonQuery();

            count++;
        }

        //close the connection
        command.Dispose();
        command = null;
        connection.Close();
        connection = null; 
    }

Однако я все еще получаю следующие ошибки:

06-08 15:26:56.498 16300-16315/? E/Unity: ArgumentException: Invalid ConnectionString format for parameter "/storage/emulated/0/Android/data/com.tbltools.tbl_project/files/data/TBLDatabase.db"
      at Mono.Data.Sqlite.SqliteConnection.ParseConnectionString (System.String connectionString) [0x00000] in <filename unknown>:0 
      at Mono.Data.Sqlite.SqliteConnection.Open () [0x00000] in <filename unknown>:0 
      at UIHandler+<RunDbCode>c__Iterator3.MoveNext () [0x00000] in <filename unknown>:0 
      at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0 
    UnityEngine.MonoBehaviour:StartCoroutineManaged2(IEnumerator)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    <RequestAllStudentNames>c__Iterator2:MoveNext()
    UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

    (Filename:  Line: -1)
06-08 15:26:56.502 16300-16315/? E/Unity: ArgumentException: Invalid ConnectionString format for parameter "URI"
      at Mono.Data.Sqlite.SqliteConnection.ParseConnectionString (System.String connectionString) [0x00000] in <filename unknown>:0 
      at Mono.Data.Sqlite.SqliteConnection.Open () [0x00000] in <filename unknown>:0 
      at UIHandler.CreateIndiButton () [0x00000] in <filename unknown>:0 
      at UIHandler+<RequestAllStudentNames>c__Iterator2.MoveNext () [0x00000] in <filename unknown>:0 
      at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0 

Я также добавил свою базу данных в папку «StreamingAssets» какпоказано на рисунке ниже:

enter image description here

Ниже также показано изображение папки моих плагинов, в которой хранятся мои DLL-файлы.

enter image description here

1 Ответ

0 голосов
/ 12 июня 2018

Большинство руководств по этой теме устарели.

Посмотрел код и обнаружил несколько проблем, но я не могу сказать, является ли это причиной того, что вы получаете эту ошибку.WWW следует использовать в функции сопрограммы, чтобы можно было завершить или дождаться окончания loadDb.isDone, добавив yield return null внутри цикла while.Вы также можете выдать запрос WWW, и этот метод я буду использовать в своем ответе.

Кроме того, jar:file://" + Application.dataPath - старый код.Для этого используйте Application.streamingAssetsPath.Кроме того, вам не нужно "URI=file:" + Application.dataPath.Просто используйте Application.persistentDataPath для этого.

Я просто добавлю инструкцию о том, как выполнить настройку.

Настройка части кода MANAGED:

1 . Перейдите на путь установки Unity

<UnityInstallationDirecory>\Editor\Data\Mono\lib\mono\2.0

Скопируйте следующие файлы:

  • I18N.MidEast.dll
  • I18N.Other.dll
  • I18N.Rare.dll
  • I18N.West.dll
  • Mono.Data.Sqlite.dll
  • Mono.Data.SqliteClient.dll
  • System.Data.dll

до проекта<ProjectName>\Assets\Plugins путь.

Это позволит вам без каких-либо ошибок скомпилировать API из пространства имен Mono.Data.Sqlite.


Настройка части кода НЕПРАВИЛЬНО:

На этом этапе вам нужно получить нативную библиотеку Sqlite.Вы можете получить исходный код , собрать его и использовать или использовать уже скомпилированный binray.

1 . Получить собственную библиотеку для Windows

Загрузите предварительно скомпилированный бит sqlite3.dll для Windows 64 из здесь и поместите его в путь <ProjectName>\Assets\Plugins\x86_64.

Если используется 32-разрядная версия Windows, тогда получите sqlite3.dll версию из здесь и поместите ее в путь <ProjectName>\Assets\Plugins\x86.


2 . Получите собственную библиотеку для Android

Загрузите предварительно скомпилированный libsqlite3.so для процессора Android ARM с здесь и поместите его в путь <ProjectName>\Assets\Plugins\Android\libs\armeabi-v7a.

Загрузите предварительно скомпилированный libsqlite3.so для Android-процессор Intel x86 с здесь и поместите его в путь <ProjectName>\Assets\Plugins\Android\libs\x86.

Это охватывает большинство процессоров , используемых на устройствах Android.


3 . Получите собственную библиотеку для UWP

A . Загрузите папку WSA, затем поместите папку WSA в путь <ProjectName>\Assets\Plugins.Эта папка содержит исходную часть.

B . Создайте 2 файла с именами "mcs.rsp" и "csc.rsp" в<ProjectName>\Assets путь.

C . Добавьте следующее в файлы "mcs.rsp" и "csc.rsp" :

-r:I18N.MidEast.dll

-r:I18N.Other.dll

-r:I18N.Rare.dll

-r:I18N.West.dll

-r:Mono.Data.Sqlite.dll

-r:Mono.Data.SqliteClient.dll

-r:System.Data.dll

D . При сборке для UWP вам придется переместить управляемые библиотеки в папку root проекта.Итак, переместите I18N.MidEast.dll, I18N.Other.dll, I18N.Rare.dll, I18N.West.dll, Mono.Data.Sqlite.dll, Mono.Data.SqliteClient.dll, System.Data.dll на путь <ProjectName> , а не <ProjectName>\Assets\Plugins.


4 . Для iOS, Linux и Mac похоже, что вам не нужно ничего загружать для них или делать этот шаг.Как правило, они имеют встроенные встроенные библиотеки Sqlite.


Включение файла базы данных в сборку:

1 . Создайте папку в папке <ProjectName>\Assets и назовите ее StreamingAssets .Правописание считается, и оно чувствительно к регистру.

2 . Поместите файл базы данных (TBLDatabase.db) в эту папку StreamingAssets .


Доступ к файлу базы данных после сборки проекта

Sqlite не может работать с файлами в папке StreamingAssets в сборке, поскольку это путь только для чтения.Кроме того, Android требует использования WWW API вместо стандартного System.IO API для чтения из папки StreamingAssets .Вам необходимо скопировать файл db из Application.streamingAssetsPath/filename.db в Application.persistentDataPath/filename.db.

На некоторых платформах необходимо создать папку внутри Application.persistentDataPath и вместо этого сохранить данные в эту папку.Всегда делай это.Папка в приведенном ниже примере кода представляет собой «данные», поэтому она станет Application.persistentDataPath/data/filename.db.

3 . Из-за приведенного выше утверждения проверьте, существует ли файл базы данных вApplication.persistentDataPath/data/filename.db.Если это так, используйте Application.persistentDataPath/data/filename.db в качестве пути для вашей базы данных.Если этого не произойдет, продолжите с # 4.

4 . Прочтите и скопируйте файл базы данных из папки StreamingAssets в Application.persistentDataPath

На некоторых платформах необходимо создать папку внутри Application.persistentDataPath и вместо этого сохранить данные в эту папку.Всегда делай это.В приведенном ниже примере папка «data».

Определите, является ли это Android, и используйте WWW для чтения файла из Application.streamingAssetsPath/filename.db.Используйте File.ReadAllBytes, чтобы прочитать его на другом устройстве, кроме Android.В вашем примере вы использовали Application.platform для этого.В моем примере я просто проверю, содержит ли путь "://" или :/// для этого.

5 . Как только вы прочитаете файл, запишите данные, которые вы только что прочиталиApplication.persistentDataPath/data/filename.db с File.WriteAllBytes.Теперь вы можете использовать этот путь для вашей операции с базой данных.

6 . Префикс "URI=file:" к пути Application.persistentDataPath/data/filename.db, и это путь, который следует использовать в вашей операции с базой данных сAPI Sqlite.


Очень важно, чтобы вы понимали все это, чтобы исправить это, когда что-то меняется, но я уже проделал шаги с # 3 до # 6 ниже.

IEnumerator RunDbCode(string fileName)
{
    //Where to copy the db to
    string dbDestination = Path.Combine(Application.persistentDataPath, "data");
    dbDestination = Path.Combine(dbDestination, fileName);

    //Check if the File do not exist then copy it
    if (!File.Exists(dbDestination))
    {
        //Where the db file is at
        string dbStreamingAsset = Path.Combine(Application.streamingAssetsPath, fileName);

        byte[] result;

        //Read the File from streamingAssets. Use WWW for Android
        if (dbStreamingAsset.Contains("://") || dbStreamingAsset.Contains(":///"))
        {
            WWW www = new WWW(dbStreamingAsset);
            yield return www;
            result = www.bytes;
        }
        else
        {
            result = File.ReadAllBytes(dbStreamingAsset);
        }
        Debug.Log("Loaded db file");

        //Create Directory if it does not exist
        if (!Directory.Exists(Path.GetDirectoryName(dbDestination)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(dbDestination));
        }

        //Copy the data to the persistentDataPath where the database API can freely access the file
        File.WriteAllBytes(dbDestination, result);
        Debug.Log("Copied db file");
    }

    try
    {
        //Tell the db final location for debugging
        Debug.Log("DB Path: " + dbDestination.Replace("/", "\\"));
        //Add "URI=file:" to the front of the url beore using it with the Sqlite API
        dbDestination = "URI=file:" + dbDestination;

        //Now you can do the database operation below
        //open db connection
        var connection = new SqliteConnection(dbDestination);
        connection.Open();

        var command = connection.CreateCommand();
        Debug.Log("Success!");
    }
    catch (Exception e)
    {
        Debug.Log("Failed: " + e.Message);
    }
}

Использование :

string dbFileName = "TBLDatabase.db";

void Start()
{
    StartCoroutine(RunDbCode(dbFileName));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...