Настройка базы данных (SQLite) для Unity

avatar
Display name
8 июня 2018 в 04:51
7307
2
7

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

Я пытаюсь включить в свой проект базу данных 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 

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

   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

Источник
Programmer
8 июня 2018 в 05:43
0

Куда вы поместили файл базы данных в своем проекте?

Display name
8 июня 2018 в 14:22
0

@Programmer Я пытался сохранить файл базы данных в папке с ресурсами, а также создать папку StreamingAssets внутри папки с ресурсами и сохранить там копию. Однако это не помогло.

Fattie
12 июня 2018 в 12:51
0

привет @Программист! - chat.coderhelper.com/rooms/info/172973 ?

Ответы (2)

avatar
Programmer
12 июня 2018 в 01:37
13

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

Просмотрел код и обнаружил несколько проблем, но я не могу сказать, являются ли они причиной этой ошибки. 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 без ошибок.


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

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

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

Загрузите предварительно скомпилированный файл sqlite3.dll для 64-разрядной версии Windows из здесь и поместите его в путь <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 для процессора Intel x86 для Android с здесь и поместите его в путь <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" и 7csc.rsp"<5072799209385038> следующее:

-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. Вам потребуется переместить управляемые библиотеки DLL в корневую папку проекта при сборке для UWP. Таким образом, движение 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 путь <ProjectName>.


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


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

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

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


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

Sqlite не может работать с файлами в папке StreamingAssets в сборке, так как это путь только для чтения. Кроме того, Android требует, чтобы вы использовали API WWW вместо стандартного API System.IO для чтения из папки StreamingAssets. Вы должны скопировать файл базы данных из 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 и вместо этого сохранять данные в эту папку. Всегда делай это. Папка в приведенном ниже примере — «данные».

Определить, является ли это 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, и этот путь следует использовать в работе базы данных с Sqlite API.


Очень важно, чтобы вы понимали все это, чтобы исправить это, когда что-то изменится, но я уже выполнил шаги с #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));
}

Programmer
12 июня 2018 в 01:40
0

Я предлагаю удалить ваш текущий плагин sqlite и папки. Удалите папки, затем начните заново. Обратите внимание, что если вы не закроете Unity, это может не сработать, потому что Unity перезапишет вашу новую dll уже загруженной старой.

Display name
12 июня 2018 в 14:06
1

Большое спасибо, что были со мной в течение последних нескольких дней, чтобы решить эту проблему. Я только что протестировал, и теперь он работает на всех платформах. Если бы я мог, я бы купил тебе пива!

Programmer
12 июня 2018 в 17:47
0

@Displayname Добро пожаловать. Я также пытался добавить поддержку WebGL, поскольку визуально ее нигде нет, но это не сработало после компиляции исходного кода c. У меня нет времени продолжать экспериментировать, но я обновлю это снова, если у меня когда-нибудь появится время снова возиться с WebGL.

Display name
12 июня 2018 в 17:50
0

Что ж, я думаю, что то, что вы сделали, — это отличный документ, который пригодится многим другим в будущем. Еще раз спасибо, и я постараюсь оставить вас в покое на некоторое время!

Programmer
12 июня 2018 в 17:52
1

Меня это не беспокоило, и да, это принесет пользу другим. Удачного кодирования.

user2088807
2 июля 2018 в 10:08
0

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

Programmer
2 июля 2018 в 10:10
0

@user2088807 user2088807 Всем сообщениям ~ 5 лет. Пожалуйста!

user2088807
17 июля 2018 в 13:03
0

Я скомпилировал Sqlite.interop.dll для x64 и x86, поместил их в свою папку Unity. Когда я создаю, он находится в папке «Плагины», и он больше не работает, пока я не поместил библиотеку в управляемую папку. Однако этот трюк, похоже, не работает с x86, и я всегда получаю обработчик Fallback, который не может загрузить библиотеку «Buil_Data/Mono/SQLite.Interop.dll». Я что-то пропустил? Почему он работает на x64, а не на x86?

Programmer
17 июля 2018 в 13:32
0

@ user2088807 Вы что-то пропустили. 1. В моем ответе нет «Sqlite.interop.dll». Не знаю, откуда ты это взял. 2.Кроме того, я не знаю, зачем вы что-то компилируете. Я связался с уже скомпилированными файлами. Я думаю, вам следует снова следовать руководству, но на этот раз не делайте того, что в нем не упомянуто.

avatar
Codemaker
16 декабря 2021 в 13:27
0

Эта ошибка возникает, когда мы пытаемся использовать SQLite в Unity 2019 или более поздних версиях. Эта ошибка возникает из-за отсутствия библиотеки Mono для Unity 2019 или более поздних версий. Вы не получите эту ошибку, если используете Unity 2018 или более ранние версии.

Если вы установили Unity 2018 или более раннюю версию, перейдите в каталог установки <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.

Или вы можете просто устранить эту ошибку, понизив версию Unity до 2018 или ниже.