How to use a file from streaming assets on android with Unity? - c#

Background information
My game is working on iOS, and now trying to update it to work on android. I have a database, Artifact.db, in a StreamingAssets folder, that is built off of SQLite and that has read-only information that I use to get data for the game.
Issue
The core issue is that the SQlite can't read the data, most likely because its not accessible from what I have been reading through on the docs. I am looking for any solutions but preferably something using the UnityWebRequest feature rather than the deprecated WWW
Working iOS Code
// Platform dependent code
string filepath = Application.streamingAssetsPath + "/Artifact.db";
string conn = "URI=file:" + filepath;
// SQlite
IDbConnection dbconn;
IDbCommand dbcmd;
IDataReader reader;
dbconn = (IDbConnection)new SqliteConnection(conn);
dbconn.Open();
dbcmd = dbconn.CreateCommand();
string sqlQuery = $"SELECT * FROM Artifact WHERE Rarity='{type}'";
dbcmd.CommandText = sqlQuery;
reader = dbcmd.ExecuteReader();
Using this code above on android results in error when doing the reader = dbcmd.ExecuteReader(); since it cannot find file.
Attempts at Android code
(Since the SQLite code itself is fine I'll only be including the platform-dependent section which deals with finding the file.)
string DatabaseName = "Artifact.db";
#if UNITY_ANDROID
string filepath = Application.persistentDataPath + "/Artifact.db";
if (!File.Exists(filepath))
{
Debug.Log("Database not in Persistent path");
// if it doesn't ->
// open StreamingAssets directory and load the db ->
var loadDb = new WWW("jar:file://" + Application.dataPath + "!/assets/" + DatabaseName); // 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(filepath, loadDb.bytes);
}
string conn = "URI=file:" + filepath;
#endif
I also tried the code from this unity forum post. (also modified some things to fit my project)
var loadingRequest = UnityWebRequest.Get(Path.Combine(Application.streamingAssetsPath, "Artifact.db"));
// Used "Artifact.db" instead of "your.bytes"
loadingRequest.SendWebRequest();
while (!loadingRequest.isDone) {
if (loadingRequest.isNetworkError || loadingRequest.isHttpError) {
break;
}
}
if (loadingRequest.isNetworkError || loadingRequest.isHttpError) {
} else {
File.WriteAllBytes(Path.Combine(Application.persistentDataPath , "Artifact.db"), loadingRequest.downloadHandler.data);
// Used "Artifact.db" instead of "your.bytes"
}
string filepath = Application.persistentDataPath + "/Artifact.db";
string conn = "URI=file:" + filepath;
What I am hoping for
I ideally hope to replicate the iOS code. I think this might mean copying the Artifact.db file to somewhere that is persistent and accessible on android and then using that file location to open the file with SQLite.

Related

SQLite database file corruption in Unity

Hi I am using SQLite as database in my Unity project. For some reason database file got corrupted but luckily I had backup so I didn't lose anything. I think the reason behind corruption was using unity collab to share database file. My question is what can I do not to corrupt the file again? Also can it corrupt after releasing the game in player's local gamefiles?
And here is the static methods i am using to execute commands, maybe something is wrong with it.
public static void Execute(string command, ref DataTable datatable)
{
string dbPath = ("Data Source=" + Application.dataPath + "/database.s3db");
using (SqliteConnection cnt = new SqliteConnection(dbPath))
{
using (SqliteCommand cmd = new SqliteCommand(command, cnt))
{
using (SqliteDataAdapter adapter = new SqliteDataAdapter(cmd))
{
adapter.Fill(datatable);
}
}
}
}
public static void Execute(string command)
{
string dbPath = ("Data Source=" + Application.dataPath + "/database.s3db");
using (SqliteConnection cnt = new SqliteConnection(dbPath))
{
using (SqliteCommand cmd = new SqliteCommand(command, cnt))
{
cnt.Open();
cmd.ExecuteNonQuery();
cnt.Close();
}
}
}
SQLite implements atomic transactions, but those guarantees hold only as long as the database file is accessed only through the SQLite library.
In your case, it's likely that the database file was copied from your friend's computer in the middle of a transaction, i.e., some changes did not end up in the published version. The only way to avoid this is to ensure that no files are currently being modified when doing the publishing.

SQL CE Not updating database values from windows forms

I am building a inventory based application that can be directly run from any pc without any installation and so i am using SQL CE.. On Start, I am checking if database is present. If Not i am creating new database as :
try
{
string conString = "Data Source='ITM.sdf';LCID=1033;Password=ABCDEF; Encrypt = TRUE;";
SqlCeEngine engine = new SqlCeEngine(conString);
engine.CreateDatabase();
return true;
}
catch (Exception e)
{
//MessageBox.Show("Database Already Present");
}
The database is created properly and i can access the database to create tables as well.. The Problem i am facing is - I am inserting and updating records in windows form on button click with code :
using (SqlCeConnection connection = new SqlCeConnection(DatabaseConnection.connectionstring))
{
connection.Open();
String Name = NameTxt.Text.ToString();
String Phone = PhoneTxt.Text.ToString();
double balance = Double.Parse(BalanceTxt.Text.ToString());
String City = CityTxt.Text.ToString();
string sqlquery = "INSERT INTO Customers (Name,Phone,Balance,City)" + "Values(#name,#phone, #bal, #city)";
SqlCeCommand cmd = new SqlCeCommand(sqlquery, connection);
cmd.Parameters.AddWithValue("#name", Name);
cmd.Parameters.AddWithValue("#phone", Phone);
cmd.Parameters.AddWithValue("#bal", balance);
cmd.Parameters.AddWithValue("#city", City);
int x = cmd.ExecuteNonQuery();
if (x > 0)
{
MessageBox.Show("Data Inserted");
}
else
{
MessageBox.Show("Error Occured. Cannot Insert the data");
}
connection.Close();
}
and Updation Code is
using (SqlCeConnection connection = new SqlCeConnection(DatabaseConnection.connectionstring))
{
connection.Open();
int idtoedit = select_id_edit;
String Name = NameEditTxt.Text.ToString();
String Phone = metroTextBox1.Text.ToString();
String City = CityEditTxt.Text.ToString();
string sqlquery = "Update Customers Set Name = #name, Phone = #phone,City = #city where Id = #id";
SqlCeCommand cmd = new SqlCeCommand(sqlquery, connection);
cmd.Parameters.AddWithValue("#name", Name);
cmd.Parameters.AddWithValue("#phone", Phone);
cmd.Parameters.AddWithValue("#id", idtoedit);
cmd.Parameters.AddWithValue("#city", City);
int x = cmd.ExecuteNonQuery();
if (x > 0)
{
MessageBox.Show("Data Updated");
}
else
{
MessageBox.Show("Error Occured. Cannot Insert the data");
}
loadIntoGrid();
connection.Close();
}
Whenever i execute code for inserting and updating records - Records are reflected in datagrid filled with adapter from database table. But once i restart the application, values do not appear in database. Once in a million, values are reflected in database. I cannot understand the reason behind this issue.
I have reffered to these articles :
C# - ExecuteNonQuery() isn't working with SQL Server CE
Insert, Update, Delete are not applied on SQL Server CE database file for Windows Mobile
But since i am creating database programmatically - It is getting created directly to bin/debug directory and i cannot see it in solution explorer in visual studio for changing copy options
You probably rewrite your database file with a blank copy from your project.
See this answer. You should not store your database in a bin folder, rather you should find a place in user's or public profile in AppData or another folder, depending on your needs.
The connection string would look like this:
<connectionStrings>
<add name="ITMContext" connectionString="data source=|DataDirectory|\ITM.sdf';LCID=1033;Password=ABCDEF; Encrypt = TRUE;" providerName="System.Data.SqlServerCe.4.0">
You shuld deploy your db file with your custom code run at app's starup to a chosen location, see this ErikEJ blog: http://erikej.blogspot.cz/2013/11/entity-framework-6-sql-server-compact-4_25.html
private const string dbFileName = "Chinook.sdf";
private static void CreateIfNotExists(string fileName)
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
// Set the data directory to the users %AppData% folder
// So the database file will be placed in: C:\\Users\\<Username>\\AppData\\Roaming\\
AppDomain.CurrentDomain.SetData("DataDirectory", path);
// Enure that the database file is present
if (!System.IO.File.Exists(System.IO.Path.Combine(path, fileName)))
{
//Get path to our .exe, which also has a copy of the database file
var exePath = System.IO.Path.GetDirectoryName(
new Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);
//Copy the file from the .exe location to the %AppData% folder
System.IO.File.Copy(
System.IO.Path.Combine(exePath, fileName),
System.IO.Path.Combine(path, fileName));
}
}
Check also this post.
You have several options to change this behavior. If your sdf file is
part of the content of your project, this will affect how data is
persisted. Remember that when you debug, all output of your project
(including the sdf) if in the bin/debug folder.
You can decide not to include the sdf file as part of your project and manage the file location runtime.
If you are using "copy if newer", and project changes you make to the database will overwrite any runtime/debug changes.
If you are using "Do not copy", you will have to specify the location in code (as two levels above where your program is running).
If you have "Copy always", any changes made during runtime will always be overwritten
The database file in your project can be different when debugging or not. Check also this answer. You might be writting your records to another copy of the database file.

Sqlite c# creates 2 same named databases

I have a problem that when i want to create database and specify its name it is created in the specified directory and in directory where the aplication is running. Why is that happening?
Code that creates database:
using System.Data.SQLite;
...
private static string AddDb(string dbName, string dbPassword)
{
try
{
//default paths
string startupPath = Environment.CurrentDirectory;
string dataBasePath = startupPath + "\\DB\\" + dbName;
//creating the dbfile
SQLiteConnection.CreateFile(dataBasePath);
//Opening connection
SQLiteConnection dbConnString;
dbConnString = new SQLiteConnection("Data Source =" + dbName + ";Version=3;");
dbConnString.Open();
dbConnString.ChangePassword(dbPassword);
dbConnString.Close();
return dataBasePath;
}
catch
{
MessageBox.Show("Failed to create database", "DB Creator");
return "";
}
}
The problem seems to be that you use different paths in CreateFile and your connection string.
If you look at your code below you'll notice that in one case you use a full path to create the file (databaseBasePath), while in the other case you only use the database file name in your connection string (dbName). Without an absolute path, this may be a different folder!
string dataBasePath = startupPath + "\\DB\\" + dbName;
SQLiteConnection.CreateFile(dataBasePath);
SQLiteConnection dbConnString;
dbConnString = new SQLiteConnection("Data Source =" + dbName + ";Version=3;");
It seems like Open then creates the file if it can't find it.
A word on the paths:
You're not allowed to write the Program Files folder, so using the current folder for the database file is really a bad idea.
The use of Environment.CurrentDirectory is also a bad idea. Depending on how you start your application, this may or may not be the folder that you think it is (see my comments to the other answers and this).
Never assume that \ is actually the path separator. Use Path.Combine instead.
I suggest that you use Environment.GetFolderPath to get a location that's shared among all users (if the database content should be shared) or private to the current user (if all users should have their own database) and create the database there:
string baseFolder = Environment.GetFolderPath(<wherever it should be>);
string dataBasePath = Path.Combine(baseFolder, "DB", dbName);
SQLiteConnection.CreateFile(dataBasePath);
SQLiteConnection dbConnString = new SQLiteConnection(String.Format("Data Source = {0};Version=3;", dataBasePath);
The Environment.CurrentDirectory contains the directory that the application starts from by default. You can set this property. See the MSDN article Environment.CurrentDirectory Property

how to connect a sqlite DB in unity 3d 5?(no plugins)

Hello i'm working on an android game project in unity3d 5 (language c#)that needs to save some data to sqlite database.
the game doesn't have a lot of data to store it has only 1 table .there are some plugins for unity that connect to sqlite but i don't want to use them .
so how can i store my data on sqlite file in unity ?
You can find your answer here
Create new folder under Assets Folder Rename it Plugins .
Copy sqlite3.def and sqlite3.dll into Assets/Plugins in your unity project .You can download these files here
http://www.sqlite.org/download.html for windows (Precompiled Binaries
for Windows)
Download SQLite Browser http://sourceforge.net/projects/sqlitebrowser/ or
http://sqliteadmin.orbmu2k.de/ download SQLite Administrator tool
Create Database in Assets folder in your unity project using SQLite Browser.
Copy System.Data.dll and Mono.Data.Sqlite.dll from **C:\Program Files (x86)\Unity \Editor\Data\Mono\lib\mono\2.0* and paste them in your Assets/Plugins* folder in your unity project.
Add these namespaces using Mono.Data.Sqlite; using System.Data; using System;
string conn= "URI=file:" + Application.dataPath + "/PickAndPlaceDatabase.s3db";
Replace PickAndPlaceDatabase.s3db with your database name
void Start () {
string conn = "URI=file:" + Application.dataPath + "/PickAndPlaceDatabase.s3db"; //Path to database.
IDbConnection dbconn;
dbconn = (IDbConnection) new SqliteConnection(conn);
dbconn.Open(); //Open connection to the database.
IDbCommand dbcmd = dbconn.CreateCommand();
string sqlQuery = "SELECT value,name, randomSequence " + "FROM PlaceSequence";
dbcmd.CommandText = sqlQuery;
IDataReader reader = dbcmd.ExecuteReader();
while (reader.Read())
{
int value = reader.GetInt32(0);
string name = reader.GetString(1);
int rand = reader.GetInt32(2);
Debug.Log( "value= "+value+" name ="+name+" random ="+ rand);
}
reader.Close();
reader = null;
dbcmd.Dispose();
dbcmd = null;
dbconn.Close();
dbconn = null;
}
Further SQLite Help Visit : http://www.tutorialspoint.com/sqlite/

Accessing folder in console application

I'm trying to write a function to read csv contents into a datatable.
I'm getting an exception about the file path and wondering what it is that I'm doing wrong.
All I did was create the console app and create a folder in the project called 'Data'.
public DataTable ReadCSV(string filename)
{
DataTable dt = new DataTable();
string sql = "SELECT * FROM " + filename;
string path = "Data\\";
string connstring = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + path + filename + ";" + "Extended Properties='text;FMT=Delimited(;);HDR=YES'";
OleDbConnection conn = new System.Data.OleDb.OleDbConnection(connstring);
System.Data.OleDb.OleDbDataAdapter da = new OleDbDataAdapter(sql, conn);
try
{
conn.Open();
da.Fill(dt);
}
catch (Exception ex)
{
Console.WriteLine(filename + "not found");
}
finally
{
conn.Close();
}
return dt;
}
}
My connection string in the Text visualizer when I run in debug mode:
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Data\Positions.csv;Extended Properties='text;FMT=Delimited(;);HDR=YES'
I'm getting an exception
base {System.Data.Common.DbException} = {"'Data\Positions.csv' is not a valid path. Make sure that the path name is spelled correctly and that you are connected to the server on which the file resides."}
Can anyone point me in the right direction? I have limited experience doing console apps so it's probably some formatting mistake that I've made. Thanks
and create a folder in the project called 'Data'.
That doesn't work. Your program is running in the bin\Debug subdirectory of your project. It doesn't have a Data subdirectory. You'd have to use ..\..\Data\Positions.csv to find that file.
Well, that would solve your problem right now but it isn't going to be useful once you copy your program to another machine. There won't be a ..\..\Data directory there. Think about ways that your user is going to tell you where the .csv file is located. A GUI with OpenFileDialog is the friendly way but not very compatible with a console app. The standard way for that is to pass command line arguments. Environment.CommandLine. Not very compatible with the typical user. You'll have to weigh these options by yourself.

Categories