Android - Could not open database in Xamarin.forms (SQLite exceptions) - c#

Sorry if the question is asked before, but i searched very well to handle this problem and i didn't find an answer.
I am handling local database with SQLite in my Xamarin forms project (PCL).
1- The connection is working well in iOS but in android i got this problem
(Could not open database file )
I also set "ReadExternalStoarage" and "WriteExternalStoarage" permissions.
2- I used another method of creating the connection path which is :
string documentsPath = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal);
string path = Path.Combine(documentsPath, "pbcare.db");
by this way the exception happened when dealing with the database ...
public bool checkLogin (string email, string password)
{
if (DB.Table<User> ().Where (user => user.Email == email && user.Password == password)
.FirstOrDefault () != null) { // exception caught here
return true;
} else {
return false;
}
}
but the table is there in the database.
Note: this second way create the connection even if the database file doesn't exist !

Android won't be able to use a SQLite database with a file from your local system. The path variable has to come from the Android system. The second approach you used to create the path was correct:
var path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
path = Path.combine(path, 'pbcare.db3');
Will create an appropriate file path for the db3 file on Android.
The next issue you reference: no such table: User is caused by not creating the table. Before you use the database, you need to create all of the necessary tables.
var conn = new SQLiteConnection(path);
conn.CreateTable<User>();
If you do this and create the User table first, then it should work as expected. There is a more in depth tutorial from Xamarin here: https://developer.xamarin.com/guides/cross-platform/application_fundamentals/data/part_3_using_sqlite_orm/

Related

Could not open database file (Misuse) SqlLiteAsyncConnection

I am getting the exception "Could not open database file: [path] (Misuse)" when trying to open my SQLite connection.
I'm creating a Xamarin.Forms application and am doing the debugging in UWP which is where I'm getting the exception.
The constructor to my data store class creates the connection and the tables:
internal static string DBPath
{
get
{
const string FILE_NAME = "TheRandomizer.db3";
if (Device.RuntimePlatform == Device.Android)
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), FILE_NAME);
}
else
{
return Path.Combine(ApplicationData.Current.LocalFolder.Path, FILE_NAME);
}
}
}
public SqliteDataStore()
{
_database = new SQLiteAsyncConnection(DBPath, SQLiteOpenFlags.Create);
// Fails trying to perform this action:
_database.CreateTableAsync<GeneratorTable>().Wait();
_database.CreateTableAsync<TagTable>().Wait();
}
The full source can be viewed here on my GitHub.
Stack trace:
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout,
CancellationToken cancellationToken) at
System.Threading.Tasks.Task.Wait() at
TheRandomizer.Services.SqliteDataStore..ctor() at
TheRandomizer.ViewModels.BaseViewModel.get_DataStore() at
TheRandomizer.ViewModels.GeneratorListViewModel.ExecuteLoadItemsCommand()
Update
I have tested your code locally and saw the behavior is actually happening and after some debugging I think I might have two reasons:
First, the constructor has only the SQLiteOpenFlags.Create flag. Apparently this does not give you any other permissions including read/write. Instead, you can either omit this second argument altogether:
_database = new SQLiteAsyncConnection(DBPath);
Or include explicit ReadWrite flag (I also included the FullMutex flag as it is recommended for async connection):
_database = new SQLiteAsyncConnection(
DBPath,
SQLiteOpenFlags.Create |
SQLiteOpenFlags.FullMutex |
SQLiteOpenFlags.ReadWrite );
Second problem occurs when creating the GeneratorTable table in DB. SQLite does not know how to store the Version property as it is a custom GeneratorVersion type. So you will probably have to break it down to simple properties or add an [Ignore] attribute.
Original answer
I have checked your source code and found out you are trying to store the database in the Environment.SpecialFolder.Personal folder. For UWP this actually resolves to C:\Users\$Username$\Documents, which a UWP app does not have access to as it is running in a sandbox and does not have access to.
Instead, you must use the application's data folder (which you probably actually intended to):
Path.Combine(ApplicationData.Current.LocalFolder.Path, FILE_NAME);

Securely Storing Database Password

I'd like to randomly generate an encryption key and password for an SQL Server CE database when it's created, and then save the key in some secure way that would allow the program to open a connection, but not be easily reachable by potential attackers.
I'm working on an offline WPF application that stores certain user and setting information in a local database.
My current implementation is to have one "Device Password" that the user sets up which is used as the encryption key for the generated SQL Server CE database password. The base64 encrypted database password is then saved in a simple .txt settings file. When the application starts up, the user enters the Device Password and that string is used as the decryption key for the saved password. If the resulting string is able to open a connection to the database, the password was correct and the program is opened with full access.
What I'm trying to do now is modify the system to allow multiple users with specific Username/Password credentials to open the program and access the database with varying levels of privilege. The way that I'm trying to achieve this is by handling the user authentication separately, and opening the database regardless of the credentials to load some basic application info.
Below is roughly my current implementation:
var candidateDBPwd = DecryptDatabasePassword(passwordBox.Password, Settings.Instance.EncryptedDatabasePassword);
if (candidateDBPwd.IsNullOrEmpty())
{
// User's password didn't decrypt database password.
return false;
}
if (File.Exists(Constants.DB_FILE))
{
// Normal operation: Try to open the database file to see that
// we've got the correct password.
string databaseArguments = Constants.DB_ARGS_SECURE + candidateDBPwd;
using (var conn = new SqlCeConnection(databaseArguments))
{
try
{
conn.Open();
}
catch (System.Data.SqlServerCe.SqlCeException ex)
{
// Failed to open the database: User's password must have been wrong!
return false;
}
}
I've spent the past few hours researching similar issues and am now beginning to wonder if it's possible. Consensus seems to state that storing passwords or connectionStrings in the App.config file is futile because if you encrypt the sections, you still need to store that key somewhere in code. Most of the existing SO threads on the issue seem to be several years out of date and it seems that that practice has deprecated. Is there some new respectable way to store a local database password? Or would you recommend a different approach to implementing the feature?
For you information here is the code snippet that can be used to encrypt certain sections of app.config. This is machine specific encryption and I think it is most simple and straightforward way to go.
I am using this with Click-once app, so that the config sections are encrypted during the first launch of the app. It means, that it is unecrypted on the publish server, it is downloaded also unencrypted and it is encrypted right after the installation finishes and application is started.
So using this method you have to distribute your files unencrypted and they are enrypted only after the installation is completed. I suppose it can be achieved by running this code during install, it depends on how you plan to install your app.
Also you can use UnprotectSection() to unencrypt previously encrypted section.
static void EncryptConfig()
{
// Encrypt config for ClickOnce deploys on first run
// ClickOnce deploys config into 2 dirs, so the parent dir is traversed to encrypt all
if (ApplicationDeployment.IsNetworkDeployed)
{
// Get paths
Assembly asm = Assembly.GetExecutingAssembly();
string exeName = Path.GetFileName(asm.Location);
string configName = exeName + ".config";
DirectoryInfo parentPath = Directory.GetParent(Directory.GetCurrentDirectory());
// Protect config files
foreach (DirectoryInfo dir in parentPath.GetDirectories())
{
foreach (FileInfo fil in dir.GetFiles())
{
if (fil.Name == configName)
{
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = fil.FullName;
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
ProtectSection(config, "connectionStrings");
config.Save(ConfigurationSaveMode.Modified);
}
}
}
}
}
private static void ProtectSection(Configuration config, string sectionName)
{
ConfigurationSection section = config.GetSection(sectionName);
if (section != null)
{
if (!section.SectionInformation.IsProtected)
{
section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
}
section.SectionInformation.ForceSave = true;
}
else
Tools.LogWarning("Section {1} not found in {0}.",config.FilePath, sectionName);
}
You can store it in registry editor. You mention that your system is offline wpf application .

Azman gives same error using either local xml or SQL Server for storage

Recently two users in our system started getting this error when trying to add them to a role.
System.Runtime.InteropServices.COMException: Cannot create a file when that file already exists. (Exception from HRESULT: 0x800700B7)
What is interesting is that the same error occurs regardless of the configuration, running locally we use an XML store and in the test environment it uses SQL Server.
Here is code where is blows up - AddMemberName() - as you can see this is pretty straightforward stuff and it's worked well for a while, it's just these two users all of the sudden
public void AddUserToRole(string roleName, string userName, bool upn)
{
string uName = userName;
if (upn)
uName = getAltUserNames(userName).First();
AzAuthorizationStore store = new AzAuthorizationStoreClass();
store.Initialize(2, _provider.StoreLocation, null);
IAzApplication app = store.OpenApplication(_provider.ApplicationName, null);
IAzRole role = app.OpenRole(roleName, null);
role.AddMemberName(uName, null);
role.Submit(0, null);
Marshal.FinalReleaseComObject(role);
}
I tried googling various different terms but can't find much of anything regarding this. I did read this post but it doesn't seem to be the issue.
Thanks
Check you Active Directory usernames and the underlying OU name especially. Check for duplicates and mismatches.
I had an issue once where a user got married and her name changed.

How do you get ASP.NET to insert a database record when a user logs in?

All I want to do, is insert to a database the username, log in time, and IP address of a user when they log in. This would literally take a few lines of code in PHP, but after requesting help in several forums and trying many different solutions it seems to be an impossible task for ASP.
The closest I've gotten is adding this to the front aspx page (where it redirects after logging in):
if (Request.IsAuthenticated)
The problem is I can not get it to do an insert after that to save my life. Selects into grid view? No problem. Insert data on page load? No way.
I can not believe that no one has ever encountered this before.
Other notes
The site will not load into Visual Studio.
I've attempted to edit the .cs files in the temp directory, but nothing seems to happen
I'm editing the .aspx files in the webroot directory.
I'd be more than happy to use straight C#, but I have no idea how to add to the middle of an aspx page
Sounds like you're working with a pre-compiled site - so, editing the code is doing nothing?
If you have complete control over the code, then it should be a simple matter to do an insert into a database in that event handler - we do that all the time. Are you using standard ADO code or trying to use some databound control or something? Just a straight up ADO call would be enough...
You know... this stuff...
SqlConnection connection = new SqlConnection(connectionString))
SqlCommand command = new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteQuery();
You could use an ASP.NET membership provider, if you're using SQL-Server you can inherit from SqlMembershipProvider.
Then you could override ValidateUser and log if it returns false(or always).
bool isValid = base.ValidateUser(username, password);
You can get the ip in the following way:
public static void logWrongPasswordAttempt(string userName, string passWord)
{
// Look for a proxy address first
var IP = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
//Trim and lowercase IP if not null
if ((IP != null))
{
IP = IP.ToLower().Trim();
}
if (IP == null || IP.Equals("unknown"))
{
//If IP is null use different detection method else pull the correct IP from list.
IP = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"].ToLower().Trim();
}
List<string> IPs = null;
if (IP.IndexOf(",") > -1)
{
IPs = IP.Split(new[]{','}, StringSplitOptions.None).ToList();
}
else
{
IPs = new List<String>() { IP };
}
foreach (string ip in IPs)
{
// insert your record into database
}
}

How to add PDF files path to SQL database and access them ? ( C# )

I have to build a software which can search and display PDF files from a folder on the local network.
My supervisor was requesting to have a database and store the path of PDF files there(not to the PDF itself because of the large amount), so I can search and open them from the software.
I would greatly appreciate if you could give me some ideas for solving it.
Haven't you already solved it? You could have an application such as a service or even a command line application that could poll/manually look into a specified folder, from there you would get the full file location and persist this into your database. When you need to search for the PDF you could perform a query against your database for PDF's that match your criteria.
You could even strip the filename from the filepath and query on that rather than the filepath and store the filepath in a different column (providing you're using a relational database).
Edit
Based on your comment on the other answer, I would stick with SQL server. From what I understand you want to automatically "catologue" the PDFs dropped in that folder, you can do this by writing a simple windows service. From that Windows service you can use an ORM (like Entity Framework) or ADO.net to persist your changes to your database. From the application you wish to display the result (be it Web app or a Win forms application, whatever), you can just query the correct column in your DB
Resources:
Entity Framework
Linq to Entities
private string[,] GetImagesFromServerFolder()
{
IntPtr token;
if (
!NativeMethods.LogonUser([Server_Login_Name], [Server_Location],
[Server_Login_Password], NativeMethods.LogonType.NewCredentials,
NativeMethods.LogonProvider.Default, out token))
{
throw new Win32Exception();
}
try
{
IntPtr tokenDuplicate;
if (!NativeMethods.DuplicateToken(
token,
NativeMethods.SecurityImpersonationLevel.Impersonation,
out tokenDuplicate))
{
throw new Win32Exception();
}
try
{
using (WindowsImpersonationContext impersonationContext =
new WindowsIdentity(tokenDuplicate).Impersonate())
{
/******************* CODE FROM HERE *******************/
List<string> files = new List<string>(Directory.GetFiles(_PHYSICAL SERVER LOCATION_));
return files;
/******************* CODE TO HERE *******************/
}
}
finally
{
if (tokenDuplicate != IntPtr.Zero)
{
if (!NativeMethods.CloseHandle(tokenDuplicate))
{
// Uncomment if you need to know this case.
////throw new Win32Exception();
}
}
}
}
finally
{
if (token != IntPtr.Zero)
{
if (!NativeMethods.CloseHandle(token))
{
// Uncomment if you need to know this case.
////throw new Win32Exception();
}
}
}
}
This will then return a list of all the .pdf files and locations
You can then sore that in a DB.
Then, run through all the files in the list (In your main running method);
List<string> file = GetImagesFromServerFolder();
foreach (var s in file)
{
const string connStr = "INSERT INTO tblPdfLocations (location) VALUES (#location)";
//Store the connection details as a string
string connstr =
String.Format(#"SERVER=[LOCATION]; UID=[USERNAME]; pwd=[PASSWORD]; Database=[DATABASE]");
//Initialise the connection to the server using the connection string.
_sqlConn = new SqlConnection(connstr);
var sqlComm = new SqlCommand(connStr, _sqlConn) {CommandType = CommandType.Text};
sqlComm.Parameters.Add(new SqlParameter("#location", SqlDbType.VarChar, 50)).Value = s;
sqlComm.ExecuteNonQuery();
_sqlConn.Close();
}
read about some ORM to work with database. For example, Entity Framework.
I would like to offer a different perspective than the other answers provided because - the storage and retrieval of such documents like PDF, Word, JPEG etc files is well into the realms of "Content Management" (CM) applications. Although relatively new in the big world of IT - these applications are mature and offer industrial strength solutions.
They use database back-ends and offer a host of facilities - the one you are interested in is the usage of CM purely as a content repository.
You don't have to look at the offerings from the big vendors (ie. Oracle Webcenter) - there are free and well supported alternatives such as Drupal and Joomla. Each of them have API's which I assume will allow you to connect into their repositories from third-party applications.
You should be looking at a solution which can grow with your company's requirements and not look to reinvent something which is already out there.

Categories