Database not persisted between builds with xamarin ios - ios

I'm creating a simple sqlite driven app for ios using xamarin studio on a mac.
The sqlite file is created in the "personal" folder and is persisted between builds but when i run the app the tables i created in the previous debug session is gone?
In my code, after checking that the file exists, i connect using a sqliteconnection and create a table and insert a row with the executenonquery method from the command object. While in the same context i can query the table using a second command object but if i stop the debugger and restart the table i gone?
Should i have the file in a different folder, is it a setting in xamarin or ios to keep the tables? Am i unintentionally using temp tables in sqlite or what could be the problem?
Note: so far i'm only using starter version of xamarin and debugging on iphone simulator.
public class BaseHandler
{
private static bool DbIsUpToDate { get; set; }
const int DB_VERSION = 1; //Created DB
const string DB_NAME = "mydb.db3";
protected const string CNN_STRING = "Data Source=" + DB_NAME + ";Version=3";
public BaseHandler ()
{
//No need to validate database more than once on each restart.
if (DbIsUpToDate)
return;
CheckAndCreateDatabase(DB_NAME);
int userVersion = GetUserVersion();
UpdateDBToVersion(userVersion);
DbIsUpToDate = true;
}
int GetUserVersion()
{
int version = 0;
using (var cnn = new SqliteConnection(CNN_STRING))
{
cnn.Open();
using (var cmd = cnn.CreateCommand())
{
cmd.CommandText = "CREATE TABLE UVERSION (VERSION INTEGER);" +
"INSERT INTO UVERSION (VERSION) VALUES(1);";
cmd.ExecuteNonQuery();
}
using (var cmd = cnn.CreateCommand())
{
cmd.CommandText = "SELECT VERSION FROM UVERSION;";
var pragma = cmd.ExecuteScalar();
version = Convert.ToInt32((long)pragma);
}
}
return version;
}
void UpdateDBToVersion(int userVersion)
{
//Prepare the sql statements depending on the users current verion
var sqls = new List<string> ();
if (userVersion < 1)
{
sqls.Add("CREATE TABLE IF NOT EXISTS MYTABLE ("
+ " ID INTEGER PRIMARY KEY, "
+ " NAME TEXT, "
+ " DESC TEXT "
+ ");");
}
//Execute the update statements
using (var cnn = new SqliteConnection(CNN_STRING))
{
cnn.Open();
using (var trans = cnn.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
{
foreach(string sql in sqls)
{
using (var cmd = cnn.CreateCommand())
{
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}
}
trans.Commit();
//SetUserVersion(DB_VERSION);
}
}
}
protected string GetDBPath (string dbName)
{
// get a reference to the documents folder
var documents = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
// create the db path
string db = Path.Combine (documents, dbName);
return db;
}
protected void CheckAndCreateDatabase (string dbName)
{
var dbPath = GetDBPath(dbName);
// determine whether or not the database exists
bool dbExists = File.Exists(dbPath);
if (!dbExists)
SqliteConnection.CreateFile(dbPath);
}
}
Again, my problem is that every time I run the debugger it runs GetUserVersion but the table UVERSION is not persisted between sessions. The "File.Exists(dbPath)" returns true so CreateFile is not run. Why is the db empty?

This is a code snippet I've used to save my databases in the iOS simulator and the data seems to persist between app compiles just fine:
string documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
string libraryPath = Path.Combine (documentsPath, "../Library/");
var path = Path.Combine (libraryPath, "MyDatabase.db3");
You may also want to check out the SQLite class for Xamarin off of Github:
https://github.com/praeclarum/sqlite-net/tree/master/src
Here's a tutorial on how to use said class:
http://docs.xamarin.com/recipes/ios/data/sqlite/create_a_database_with_sqlitenet

Turns out that I was creating the connection object using the CNN_STRING which just had the db-name instead of the full path to the db. Apperantly the connection object creates the database if the file doesn't exist so the File.Exists(...) might not be needed. I'm not really sure if it should be a temporary db if the complete path is not supplied but it seems to be the case. Changing the creation of the connection object to "datasource=" solved the problem.

Related

Flutter iOS App sqflite database data lost after app upgrade

We have an app build entirely by flutter. When we send app upgrades to google store the sqflite database data is not lost when updating apk in Android device.
But in iOS Device after user updates app to new version all database data lost.
flutter v1.17.5
Package used for data persistence:
sqflite: ^1.3.1
path_provider: ^1.6.11
Can you please help to solve this problem. Data of user must not be deleted after app update to new version. Maybe I need to use other type of data persistence or how can I solve this.
Here are parts where I initialize Database in DatabaseHelper class:
class DatabaseHelper {
static DatabaseHelper _databaseHelper; // Singletone DatabaseHelper
static Database _database; // Singletone Database
String wifiSystemTable = 'wifi_system_table';
String colId = 'id';
String colDataType = 'data_type';
String colLocationName = 'location_name';
String colBackground = 'background';
String colLocationId = 'location_id';
String colDeviceType = 'device_type';
String colIp = 'ip';
String colName1 = 'name1';
String colName2 = 'name2';
String colName3 = 'name3';
String colRemotePort = 'remote_port';
DatabaseHelper._createInstance(); // Named constractor to create instance of Database Helper
factory DatabaseHelper() {
if (_databaseHelper == null) {
_databaseHelper = DatabaseHelper
._createInstance(); //This is execute only once, singletone object
}
return _databaseHelper;
}
Future<Database> get database async {
if (_database == null) {
_database = await initializeDatabase();
}
return _database;
}
Future<Database> initializeDatabase() async {
//Get the directory path for both Android and IOS to store Database
Directory directory = await getApplicationDocumentsDirectory();
String path = p.join(directory.toString(), 'wifi.db');
//Open/create the database at the given path
var wifiSystemDatabase =
await openDatabase(path, version: 1, onCreate: _createDb);
return wifiSystemDatabase;
}
void _createDb(Database db, int newVersion) async {
await db.execute(
'CREATE TABLE $wifiSystemTable($colId INTEGER PRIMARY KEY, $colDataType INTEGER, $colLocationName TEXT, $colBackground TEXT, $colLocationId INTEGER, $colDeviceType INTEGER, $colIp TEXT, $colName1 TEXT, $colName2 TEXT, $colName3 TEXT, $colRemotePort TEXT)');
}
Thanks very much
I think your DB path is getting changed. Try this.
Future<Database> initializeDatabase() async {
//Get the directory path for both Android and IOS to store Database
String databasesPath = await getDatabasesPath();
String path = p.join(databasesPath, 'wifi.db');
//Open/create the database at the given path
var wifiSystemDatabase = await openDatabase(path, version: 1, onCreate: _createDb);
return wifiSystemDatabase;
}
Hope it helps :)

Creating DbCommand does get timeout assigned from DBContext

I have ASP.NET Core 2.2 application using EF Core. In startup.cs i am setting CommandTimeout to 60 seconds
services.AddDbContext<CrowdReason.WMP.Data.Entities.WMPContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
sqlServerOptions => sqlServerOptions.CommandTimeout(60)));
Then i am executing the stored proc using the following code. Please note the values of t1, t2 and t3 in comments
public static async Task<int?> prcDoWork(this WMPContext dbContext, int id, int userID)
{
var t1 = dbContext.Database.GetCommandTimeout();
// t1 is 60. Same as what i set in startup.cs
using (var connection = dbContext.Database.GetDbConnection())
{
var t2 = connection.ConnectionTimeout;
//t2 is 15
using (var cmd = connection.CreateCommand())
{
var p1 = new SqlParameter("#ID", SqlDbType.Int)
{
Value = id
};
var p2 = new SqlParameter("#UserID", SqlDbType.Int)
{
Value = userID
};
cmd.CommandText = "dbo.prcDoWork";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
var t3 = cmd.CommandTimeout;
//t3 is 30
await dbContext.Database.OpenConnectionAsync().ConfigureAwait(false);
var result = await cmd.ExecuteScalarAsync().ConfigureAwait(false);
if (result != null)
{
return Convert.ToInt32(result);
}
return null;
}
}
}
I understand that ConnectionTimeout is different from CommandTimeout.
However issue is when i create command using connection.CreateCommand() its not automatically getting the timeout from DBContext
EF Core works on top of the underlying System.Data implementation (in your case System.Data.SqlClient) and uses it to perform its DB operations. All settings you make will only reflect the way EF uses this underlying implementation.
When you use the GetDbConnection method you get a reference to an SqlConnection class from the System.Data.SqlClient assembly that knows nothing about EF and its settings and cannot be expected to honor the RelationalOptionsExtension.CommandTimeout from the Microsoft.EntityFrameworkCore.Relational assembly.
To have EF settings respected you should use the RelationalDatabaseFacadeExtensions.ExecuteSqlCommandAsync method.

Android; SQLite database Cursor is null. It should not be. I don't know why it is

I had a method which fetched records from an sqlite database but after a while i changed it a little and made a secondary method for fetching specific records with user entered information.
I don't know why but my original method is returning null now.
CardDbAdapter:
public CarddbAdapter open2() throws SQLException {
String myPath = DB_PATH + DB_NAME;
myDatabaseR = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READONLY);
myDatabaseW = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READWRITE);
return this;
}
public void MyDatabaseClose() {
myDatabaseW.close();
myDatabaseR.close();
}
public ArrayList<String> getAllCardNames() {
ArrayList<String> returnedAllCardNames;
ArrayList<String> NoResults;
ArrayList<String> NoResults2;
NoResults = new ArrayList<String>();
NoResults.add("cursor is null");
NoResults2 = new ArrayList<String>();
NoResults2.add("No similar cards found.");
returnedAllCardNames = new ArrayList<String>();
/*String sqlquery_cardNames = "SELECT " + KEY_CARDNAME
+ " FROM cards WHERE card_name like '%" + passedName
+ "%' ORDER BY card_name ASC";*/
//String sqlquery_cardNames;
String sqlquery_cardNames = "SELECT DISTINCT card_name FROM cards";
Cursor c_cardNames;
c_cardNames = myDatabaseW.rawQuery(sqlquery_cardNames, null);
c_cardNames.moveToFirst();
if (c_cardNames != null) {
do {
if (c_cardNames.getCount() > 0) {
String returnedName = c_cardNames.getString(c_cardNames
.getColumnIndex(KEY_CARDNAME));
returnedAllCardNames.add(returnedName);
} else {
return NoResults2;
}
} while (c_cardNames.moveToNext());
}
return NoResults;
}
How i am using it:
CarddbAdapter yugiohDB = new CarddbAdapter(this);
yugiohDB.open2();
search_results = (ListView) findViewById(R.id.lvSearchResults);
search_results.setOnItemClickListener(this);
ArrayList<String> returnedCards_list1 = new ArrayList<String>();
returnedCards_list1.addAll(yugiohDB.getAllCardNames());
listAdapter = new ArrayAdapter<String>(SearchMode_Simple.this,
android.R.layout.simple_list_item_1, returnedCards_list1);
search_results.setAdapter(listAdapter);
yugiohDB.MyDatabaseClose();
Any help would be appreciated.
If you would like to see what this is actually supposed to do then download my app called Yugioh Library + Tools. Click the Search Library button from the main menu and then the button Simple Search. It should be displaying a list of cards from the database.
The reason i was changing it is because i'm setting up Spinners so users can choose different trading card sets to choose some which would then list all the cards from that specific set.
Fixed. I forgot to return the string array "returnedAllCardNames" which was holding the names of all process card records after the do while loop.

Windows Application SqlDepedency Calling Onchange infinitely

I have console application in which I am doing sqldependency. My problem is when I set commandType as Text, it is working fine. But if I use commandType as StoredProcedure, onchange method is calling infinitely.
Please see the code below:
static DataSet myDataSet;
static SqlConnection connection;
static SqlCommand command;
static void Main(string[] args)
{
// Remove any existing dependency connection, then create a new one.
string connstr = "Data Source=XYZ;Initial Catalog=Dev;Integrated Security=True";
string ssql = #"[dbo].[SchedulerPendingControlRequestIDFetch]";
CanRequestNotifications();
SqlDependency.Stop(connstr);
SqlDependency.Start(connstr);
if (connection == null)
connection = new SqlConnection(connstr);
if (command == null)
command = new SqlCommand(ssql, connection);
command.CommandType = CommandType.StoredProcedure;
if (myDataSet == null)
myDataSet = new DataSet();
GetAdvtData();
System.Console.ReadKey();
connection.Close();
}
private static bool CanRequestNotifications()
{
SqlClientPermission permission =
new SqlClientPermission(
PermissionState.Unrestricted);
try
{
permission.Demand();
return true;
}
catch (System.Exception)
{
return false;
}
}
private static void GetAdvtData()
{
myDataSet.Clear();
// Ensure the command object does not have a notification object.
command.Notification = null;
// Create and bind the SqlDependency object to the command object.
SqlDependency dependency = new SqlDependency(command,null,100);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(myDataSet, "ControlRequest");
}
}
private static void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency =
(SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
Console.WriteLine(e.Info.ToString() + e.Source.ToString());
GetAdvtData();
}
My stored Procedure is:
IF OBJECT_ID('SchedulerSirasColcoDetailFetch') IS NOT NULL
DROP PROCEDURE SchedulerSirasColcoDetailFetch
Go
PRINT 'Creating stored procedure SchedulerSirasColcoDetailFetch'
Go
CREATE PROCEDURE [dbo].[SchedulerSirasColcoDetailFetch]
AS
BEGIN
SELECT Colco_Code AS 'CountryCode',Connection_String AS 'Url',Resend_Interval AS 'ResendInterval',
Default_Encoding AS 'Encoding' FROM dbo.SirasColcoDetail
END
If I copy the select statement inside stored procedure as my command text and set the commandType as Text, everything is working fine.
could you please let me know what the issue is????
Thanks a lot in advance.
Mahesh
You're supposed to check the values of the SqlNotificationEventArgs argument. Only if Type is Change and Source is Data where you notified for a data change.
You'll discover that you're not notified for data changes, but for incorrect settings or incorrect query. Your query and connection settings must comply with the requirements specified in Creating a Query for Notifications.

VSTO 3.0 Get/Change an excel 2007 workbook connection

I've struggling to find a way to get and change and excel 2007 workbook connection (Menu Data -> Existing Connections -> Connections on this Workbook).It's a connection (several actually) to a SQL Server and used in a pivot table.
I've tried using Application.ActiveWorkbook.Connections or Globals.ThisWorkbook.Connections but they both return always Null..I've tried in an sheet event as well as in a custom ribbon's button event as well.
The only way left I can think of is use to code a VBA method that does the work and then invoke it in my VSTO code, but it's not very elegant is it...
Existing connections in Excel (this works in 2007) are not active connections. You must connect using an existing connection before being able to acquire that connection (I did this manually before clicking a button that executed this code).
var application = Globals.ThisAddIn.Application;
//This must be an active connection otherwise handle exceptions
// such as 'Invalid index. (Exception from HRESULT: 0x8002000B (DISP_E_BADINDEX))'
var connection = application.ActiveWorkbook.Connections["EXISTING_CONNECTION_NAME"];
var oledb = connection.OLEDBConnection;
var settings = oledb.Connection;
Here I adjust connection string of Excel Connections. Take in account that I have only ONE connection in Workbook.
public class WorkbookConnectionsManager
{
public static void AdjustConnectionToSqlConnectionString(Excel.WorkbookConnection connection, String connectionString)
{
char[] propertiesSeparator = new char[] { ';' };
char[] propertyValueSeparator = new char[] { '=' };
Excel.OLEDBConnection oleDbConn = connection.OLEDBConnection;
Dictionary<string, string> dictExcelConnStrProperties = GetConnStrDictionary(oleDbConn.Connection, propertiesSeparator, propertyValueSeparator);
Dictionary<string, string> dictActualConnStrProperties = GetConnStrDictionary(connectionString, propertiesSeparator, propertyValueSeparator);
string[] reggedPropertyies = new string[] { "Integrated Security", "Persist Security Info", "User ID", "Password", "Initial Catalog", "Data Source", "Workstation ID" };
foreach (string property in reggedPropertyies)
if (dictExcelConnStrProperties.ContainsKey(property) && dictActualConnStrProperties.ContainsKey(property)
&& null != dictActualConnStrProperties[property] && !String.IsNullOrEmpty(dictActualConnStrProperties[property].ToString()))
dictExcelConnStrProperties[property] = dictActualConnStrProperties[property];
string connStr = GetConnStrFromDict(dictExcelConnStrProperties, propertiesSeparator[0], propertyValueSeparator[0]);
oleDbConn.Connection = connStr;
}
private static string GetConnStrFromDict(Dictionary<string, string> dictConnStrProperties, char propertiesSeparator, char propertyValueSeparator)
{
StringBuilder connStrBuilder = new StringBuilder();
foreach (KeyValuePair<string, string> keyValuePair in dictConnStrProperties)
{
connStrBuilder.Append(keyValuePair.Key);
if (!String.IsNullOrEmpty(keyValuePair.Value))
{
connStrBuilder.Append(propertyValueSeparator);
connStrBuilder.Append(keyValuePair.Value);
}
connStrBuilder.Append(propertiesSeparator);
}
string connStr = String.Empty;
if (connStrBuilder.Length > 1)
{
connStr = connStrBuilder.ToString(0, connStrBuilder.Length - 1);
}
return connStr;
}
private static Dictionary<string, string> GetConnStrDictionary(string connString, char[] propertiesSeparator, char[] propertyValueSeparator)
{
string[] keyAndValue;
string[] arrayConnStrProperties = connString.Split(propertiesSeparator);
Dictionary<string, string> dictConnStrProperties = new Dictionary<string, string>();
foreach (string excelConnStrProperty in arrayConnStrProperties)
{
keyAndValue = excelConnStrProperty.Split(propertyValueSeparator);
if (keyAndValue.Length > 1)
{
dictConnStrProperties.Add(keyAndValue[0], keyAndValue[1]);
}
else if (keyAndValue.Length > 0)
{
//standalone attribute
dictConnStrProperties.Add(keyAndValue[0], String.Empty);
}
}
return dictConnStrProperties;
}
}
I can't remember where but I remember reading somewhere that the Connections collection was of limited use for writing ODBC type connections. It has several enum values for a "connection" but I'm not sure if some of those are readonly from that interface.
Regardless it should be quite easy to implement new connections and edit existing ones from VSTO. The best option would be to use COM interop to call the SQLConfigDataSource() function from the ODBCCP32.DLL win32 library.
Also check out the following addin which makes it easier to work with query tables in Excel.

Resources