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);
Related
We have a number of SSRS sites serving reports to various locations. Each of these servers all have custom connections in each and every report (don't ask why, that's a tale too torrid to tell). Our goal is to replace all of these custom data sources with a single shared data source for all reports on each server.
To that end, I have created a C# program that will find each report on each server and point the current custom data sources to a currently existing shared data source. This executes and seems to work fine.
My next goal is to use C# to create the shared data source on each server where none currently exists.
My current dilemma arises here:
private static void CreateSharedDataSource(string user, string password, string connection)
{
DataSourceDefinition source = new DataSourceDefinition();
source.CredentialRetrieval = CredentialRetrievalEnum.Store;
source.ConnectString = connection;
source.Enabled = true;
source.EnabledSpecified = true;
source.Extension = "SQL";
source.ImpersonateUser = false;
source.ImpersonateUserSpecified = false;
source.Prompt = "Enter a user name and password to access the data source:";
source.UserName = user;
source.Password = password;
source.WindowsCredentials = true;
source.OriginalConnectStringExpressionBased = true;
source.UseOriginalConnectString = true;
try
{
service.CreateDataSource("DbDataSource", "/DsFolder", false, source, null);
Console.WriteLine("Data source created successfully");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
The data source is created correctly and the user name and password are updated correctly. The problem is that, when I look at the newly created data source on the server, the Connect string property is empty and, yes, the correct value is being passed to it in the method above. If I plug that value into the shared source on the server and test the connection, it works fine, but I cannot get the C# program to update that value itself.
So, is there something subtle I'm missing? Am I misinterpreting a setting up there? Did I set a value wrong?
Clues appreciated.
I've never tried anything like this but a quick bit of research uncovered this which may be helpful.
https://learn.microsoft.com/en-us/dotnet/api/reportservice2010.datasourcedefinition.originalconnectstringexpressionbased?view=sqlserver-2016
It states
"Expression-based connection strings are not supported in shared data
sources."
Assuming the conneciton string is just a plain text string then I would guess that you could set this to false. This may help preserve the string you pass in.
Alright, found my answer. I had a pre-existing data source that was working so, instead of creating it from scratch, I copied that and only changed the name. That created a data source where the Connect string did persist. Comparing the settings in that with what I was setting revealed:
source.UseOriginalConnectString = false;
whereas, my code was:
source.UseOriginalConnectString = true;
Looking that up in docs and it tells me "If true, the value of the ConnectString property is ignored."
Hmmm... that's intuitive. That's not what that sounds like at all. :)
I am building a transport agent for Microsoft Exchange server. The logic I have implemented so far works. Now, I want to store some of the variables in DB. I have opted for a repository pattern and when I try to extract a connection string from App.Config file I am receiving a NullReference Exception:
public class ConfigRepository : IConfigRepository, IDisposable
{
private string configString = System.Configuration.ConfigurationManager.ConnectionStrings["citadelEAPEntities"].ConnectionString;
// private string configString = "..."; here I tried to write the string directly .
private string configValProc = "[dbo].[GetConfigValue]";
private SqlConnection connection;
public ConfigRepository()
{
connection = new SqlConnection(configString);
}
// other logic and implementation of interfaces
}
The same class is implemented in a test console app, which works fine (with the same connection string stored in the same manner in the app.config file).
I cannot understand the reason for this exception. The transporter agent class instantiates a ConfigRepository class within a 'using' statement and the moment it reaches the constructor function upon the initialization of the connection string variable via config manager an exception gets thrown. Obviously when I use the connection string in a hard coded manner everything works just fine.
Is the transporter agent assembly somehow limited while referencing other assemblies (such as System.Configuration) ? Just weird.
Transport Agents run under the security context of NetworkService so its most likely that it doesn't have enough rights to load the assembly from the location your referencing it from. If you use something like process monitor https://learn.microsoft.com/en-us/sysinternals/downloads/procmon your should be able track the process etc.
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/
Two functions in our standard ASP.NET app are:
private static void SaveToFileSystem(AttributeFileAttachment attach, int paId)
{
string fileName = GetAttachmentFullName(attach.FileName, paId);
File.WriteAllBytes(fileName, attach.Content);
}
public static string GetAttachmentFullName(string name, int paId)
{
HttpContext ctx = Util.Util.GetHttpContext();
return string.Format("{0}{1}_{2}_{3}",
ctx.Server.MapPath("<some variable to get the path>" + "attributeFileAttachments\\"),
ctx.Session.SessionID,
paId,
name);
}
when File.WriteAllBytes is executed it returns exception:
he process cannot access the file '\\d$\Home\\attributeFileAttachments\' because it is being used by another process.
The essence are two lines:
ctx.Server.MapPath... (Microsoft code)
and File.WriteAllBytes...
that work on the same file.
It turns out that HttpServerUtility.MapPath locks the file and leaves it locked !?
I don't see any comments on that in official documentation nor I see anybody complains on that.
But it can't be anything else, since the two lines are consecutive.
When I modify fileName for File.WriteAllBytes in immediate window just a bit, the writing succeeds, since that new file is not locked.
One more thing I have noticed is that this happens only and always for some of the attachment files.
Thank you for the time and any advice.
I'm currently using this SQLite library for my console application: http://system.data.sqlite.org/index.html/doc/trunk/www/index.wiki - which has been OK so far with SELECT queries, but doing this INSERT is causing me problems, which I have not found a solution for.
I'm guessing the code could be re-worked but I cannot see how?
Code
public string NewChannel(string _channel)
{
SQLiteConnection m_dbConnection = new SQLiteConnection(m_connection);
using (var cmd = m_dbConnection.CreateCommand())
{
m_dbConnection.Open();
cmd.CommandText = "INSERT INTO channels (name) VALUES (#name)";
cmd.Parameters.AddWithValue("#name", _channel);
try
{
int result = cmd.ExecuteNonQuery();
return "New channel added: " + _channel;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
}
}
Error
SQLite error (10): delayed 1375ms for lock/sharing conflict SQLite
error (14): os_win.c:34909: (5) winOpen(c:\db.sqlite-journal) - Access
is denied. SQLite error (14): os_win.c:34909: (2)
winOpen(c:\db.sqlite-journal) - The system cannot find the file
specified. SQLite error (14): cannot open file at line 34917 of
[118a3b3569] SQLite error (14): statement aborts at 7: [INSERT INTO
channels (name) VALUES (#name)]
The particular error code represents the following:
Error Code 14: SQL Lite Database File Can't be Opened.
The reason that running Visual Studio as Administrator worked is because you elevated the Permission for Read and Write. It isn't that it is actually creating a new file, but it may be Reading or Writing data to the file.
Based on particular permissions that may be restricted based on the given role, unless the permissions have been explicitly defined.
As I mentioned above the root C: requires Power User or Above in order to Write data on the root. Which when your database attempts to append, is considered Write which it may not be able to do.
Solution One: Explicitly define permission for application / user to avoid issue.
Solution Two: Write a check before it tries to access the data file to ensure proper permission.
An example of such a check:
public ValidFolderPermission(string filePath)
{
if(File.Exist(filePath))
{
// Open Stream and Read.
using (FileStream fs = File.Open(filePath, FileMode.Open))
{
byte[] data = new byte[1024];
UTF8Encoding temp = new UTF8Encoding(true);
while (fs.Read(b, 0, b.Length) > 0)
{
Console.WriteLine(temp.GetString(b));
}
}
}
}
In this case if it actually opens the file in that path, you have proper access. If it doesn't, then you don't have permission.
Important: To really test you should utilize a try and catch, even possibly boolean returns to ensure it did accurately handle before you write to the database.
You can even check the users permission level like this:
WindowsIdentity user = WindowsIdentity.GetCurrent();
WindowsPrincipal role = new WindowsPrincipal(user);
if(role.IsInRole(WindowsBuiltInRole.Administrator)
{
// Do Something
}
else
{
// Do Something Else
}
That is a generic approach, but there are better more concise ones that will test for Operating System for User Access Control features, null values for User Account, and etc. handling for other issues you may encounter.
Hopefully this points you in the proper direction though.