I have a very strange case using sqlite-pcl in android platform ... I have one class for database which is inherited from SQLiteConnection, I use personal folder for my db, in constructor I have one check for existence, but often when I start application again , without deleting any files, it return false...
public class ScannifyDatabase : SQLiteConnection
{
public static string DBPath
{
get
{
string path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
return System.IO.Path.Combine(path, "Scaniff.db3");
}
}
public ScannifyDatabase(string path)
: base(new SQLitePlatformAndroid(),path)
{
if (!TableExists())
{
this.CreateTable<DocumentType>();
InitData();
}
}
private bool TableExists()
{
// return this.TableExists();
const string cmdText = "SELECT name FROM sqlite_master WHERE type='table' AND name=?";
var cmd = this.CreateCommand(cmdText, typeof(DocumentType).Name);
var res = !string.IsNullOrEmpty(cmd.ExecuteScalar<string>());
return res;
}
}
what the problem is?
In Xamarin Studio, under Preferences --> Android, be sure that you have "Preserve data/cache between application deploys" checked. There is a similar setting in Visual Studio.
Related
I want to create a shared template in which I want to check the SQLite database file is exists or not, and if the file does not exist then create a new DB file in xamarin.
//my function
public Task<CreateDatabase> DbConnection(string DbName, string DbPath)
{
string dbname = DbName.ToLowerInvariant();
string path = Path.Combine(DbPath, dbname);
var database = new CreateDatabase(path);
if (File.Exists(path) == false)
{
var database = new CreateDatabase(path);
}
return null;
}
properties
public class CreateDatabase
{
public string databasePaths { get; }
public CreateDatabase(string databasePath)
{
databasePaths = databasePath;
}
}
You could use Flags to check and create the database if the database doesn't exist.
public static class Constants
{
public const SQLite.SQLiteOpenFlags Flags =
// open the database in read/write mode
SQLite.SQLiteOpenFlags.ReadWrite |
// create the database if it doesn't exist
SQLite.SQLiteOpenFlags.Create |
// enable multi-threaded database access
SQLite.SQLiteOpenFlags.SharedCache;
}
And then set the Flags when you build the connection.
Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
You could download the sample code form the link below for reference.
https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/todo/
I am attempting to use a PostgreSQL database managed by Heroku in an ASP.NET MVC app running on .NET core 2.0. I would like to be able to use the Entity Framework to easily read and write to the database. I am extremely new to all of these things except for ASP.NET, which is likely obvious, and having previously used a local SQLite server for this same purpose, I have almost no understanding of how PostgreSQL works with Heroku and the Entity Framework.
I have installed the Npgsql extension to the Entity Framework. I am stuck at the Entity Framework's connection string for this particular setup and how to use it with Heroku. Heroku supplies a DATABASE_URL variable (documented here), which is necessary to use because the database connection credentials are subject to change and Heroku automatically updates the variable when they change.
//This method gets called by the runtime. Use this method to add services to
//the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
string connection = "???";
services.AddDbContext<MyDbContext>(options => options.UseNpgsql(connection));
}
What value do I use for connection such that it will connect to the database through Heroku's URL independently of the current credentials? Additionally, how can I ensure that a table will be created in the database matching MyDbContext's model?
My goal is simply to have a database accessible (read and write) from a deployed Heroku website. It would be nice if I could also access the database locally for development purposes, but my only requirement is that this work on the hosted website and that the database is managed by Heroku (I went with PostgreSQL, Kafka and Redis are also available through Heroku).
The solution here is to "parse" the content of the DATABASE_URLenvironment variable provided by Heroku and use it to build the connection string in the format that the Npgsql expects.
For a quick and dirty solution, you can just follow this solution: .net core - database url parser.
For my project I decided to go a little further and created a class for that, based in the other connection string builders (for MS SQL, Mongo and etc):
public enum SslMode
{
Require,
Disable,
Prefer
}
public class PostgreSqlConnectionStringBuilder : DbConnectionStringBuilder
{
private string _database;
private string _host;
private string _password;
private bool _pooling;
private int _port;
private string _username;
private bool _trustServerCertificate;
private SslMode _sslMode;
public PostgreSqlConnectionStringBuilder(string uriString)
{
ParseUri(uriString);
}
public string Database
{
get => _database;
set
{
base["database"] = value;
_database = value;
}
}
public string Host
{
get => _host;
set
{
base["host"] = value;
_host = value;
}
}
public string Password
{
get => _password;
set
{
base["password"] = value;
_password = value;
}
}
public bool Pooling
{
get => _pooling;
set
{
base["pooling"] = value;
_pooling = value;
}
}
public int Port
{
get => _port;
set
{
base["port"] = value;
_port = value;
}
}
public string Username
{
get => _username;
set
{
base["username"] = value;
_username = value;
}
}
public bool TrustServerCertificate
{
get => _trustServerCertificate;
set
{
base["trust server certificate"] = value;
_trustServerCertificate= value;
}
}
public SslMode SslMode
{
get => _sslMode;
set
{
base["ssl mode"] = value.ToString();
_sslMode = value;
}
}
public override object this[string keyword]
{
get
{
if (keyword == null) throw new ArgumentNullException(nameof(keyword));
return base[keyword.ToLower()];
}
set
{
if (keyword == null) throw new ArgumentNullException(nameof(keyword));
switch (keyword.ToLower())
{
case "host":
Host = (string) value;
break;
case "port":
Port = Convert.ToInt32(value);
break;
case "database":
Database = (string) value;
break;
case "username":
Username = (string) value;
break;
case "password":
Password = (string) value;
break;
case "pooling":
Pooling = Convert.ToBoolean(value);
break;
case "trust server certificate":
TrustServerCertificate = Convert.ToBoolean(value);
break;
case "sslmode":
SslMode = (SslMode) value;
break;
default:
throw new ArgumentException(string.Format("Invalid keyword '{0}'.", keyword));
}
}
}
public override bool ContainsKey(string keyword)
{
return base.ContainsKey(keyword.ToLower());
}
private void ParseUri(string uriString)
{
var isUri = Uri.TryCreate(uriString, UriKind.Absolute, out var uri);
if (!isUri) throw new FormatException(string.Format("'{0}' is not a valid URI.", uriString));
Host = uri.Host;
Port = uri.Port;
Database = uri.LocalPath.Substring(1);
Username = uri.UserInfo.Split(':')[0];
Password = uri.UserInfo.Split(':')[1];
}
}
And then, in my Startup.cs, in the Configuration method, I have:
var builder = new PostgreSqlConnectionStringBuilder(Configuration["DATABASE_URL"])
{
Pooling = true,
TrustServerCertificate = true,
SslMode = SslMode.Require
};
services.AddEntityFrameworkNpgsql()
.AddDbContext<TTRDbContext>(options => options.UseNpgsql(builder.ConnectionString));
If you are accessing your DB from outside Heroku network (e.g. your local environment), you need to add the SSL Mode and Trust Server Certificate.
Hope it helps
ASP.NET Core apps running in Linux containers use a case sensitive file system, which means that the CSS and JS file references must be case-correct.
However, Windows file system is not case sensitive. Therefore during development you can have CSS and JS files referenced with incorrect casing, and yet they work fine. So you won't know during development on Windows, that your app is going to break when going live on Linux servers.
Is there anyway to make Kestrel on Windows case sensitive, so that we can have consistent behaviour and find the reference bugs before going live?
I fixed that using a middleware in ASP.NET Core.
Instead of the standard app.UseStaticFiles() I used:
if (env.IsDevelopment()) app.UseStaticFilesCaseSensitive();
else app.UseStaticFiles();
And defined that method as:
/// <summary>
/// Enforces case-correct requests on Windows to make it compatible with Linux.
/// </summary>
public static IApplicationBuilder UseStaticFilesCaseSensitive(this IApplicationBuilder app)
{
var fileOptions = new StaticFileOptions
{
OnPrepareResponse = x =>
{
if (!x.File.PhysicalPath.AsFile().Exists()) return;
var requested = x.Context.Request.Path.Value;
if (requested.IsEmpty()) return;
var onDisk = x.File.PhysicalPath.AsFile().GetExactFullName().Replace("\\", "/");
if (!onDisk.EndsWith(requested))
{
throw new Exception("The requested file has incorrect casing and will fail on Linux servers." +
Environment.NewLine + "Requested:" + requested + Environment.NewLine +
"On disk: " + onDisk.Right(requested.Length));
}
}
};
return app.UseStaticFiles(fileOptions);
}
Which also uses:
public static string GetExactFullName(this FileSystemInfo #this)
{
var path = #this.FullName;
if (!File.Exists(path) && !Directory.Exists(path)) return path;
var asDirectory = new DirectoryInfo(path);
var parent = asDirectory.Parent;
if (parent == null) // Drive:
return asDirectory.Name.ToUpper();
return Path.Combine(parent.GetExactFullName(), parent.GetFileSystemInfos(asDirectory.Name)[0].Name);
}
Based on #Tratcher proposal and this blog post, here is a solution to have case aware physical file provider where you can choose to force case sensitivity or allow any casing regardless of OS.
public class CaseAwarePhysicalFileProvider : IFileProvider
{
private readonly PhysicalFileProvider _provider;
//holds all of the actual paths to the required files
private static Dictionary<string, string> _paths;
public bool CaseSensitive { get; set; } = false;
public CaseAwarePhysicalFileProvider(string root)
{
_provider = new PhysicalFileProvider(root);
_paths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
public CaseAwarePhysicalFileProvider(string root, ExclusionFilters filters)
{
_provider = new PhysicalFileProvider(root, filters);
_paths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
public IFileInfo GetFileInfo(string subpath)
{
var actualPath = GetActualFilePath(subpath);
if(CaseSensitive && actualPath != subpath) return new NotFoundFileInfo(subpath);
return _provider.GetFileInfo(actualPath);
}
public IDirectoryContents GetDirectoryContents(string subpath)
{
var actualPath = GetActualFilePath(subpath);
if(CaseSensitive && actualPath != subpath) return NotFoundDirectoryContents.Singleton;
return _provider.GetDirectoryContents(actualPath);
}
public IChangeToken Watch(string filter) => _provider.Watch(filter);
// Determines (and caches) the actual path for a file
private string GetActualFilePath(string path)
{
// Check if this has already been matched before
if (_paths.ContainsKey(path)) return _paths[path];
// Break apart the path and get the root folder to work from
var currPath = _provider.Root;
var segments = path.Split(new [] { '/' }, StringSplitOptions.RemoveEmptyEntries);
// Start stepping up the folders to replace with the correct cased folder name
for (var i = 0; i < segments.Length; i++)
{
var part = segments[i];
var last = i == segments.Length - 1;
// Ignore the root
if (part.Equals("~")) continue;
// Process the file name if this is the last segment
part = last ? GetFileName(part, currPath) : GetDirectoryName(part, currPath);
// If no matches were found, just return the original string
if (part == null) return path;
// Update the actualPath with the correct name casing
currPath = Path.Combine(currPath, part);
segments[i] = part;
}
// Save this path for later use
var actualPath = string.Join(Path.DirectorySeparatorChar, segments);
_paths.Add(path, actualPath);
return actualPath;
}
// Searches for a matching file name in the current directory regardless of case
private static string GetFileName(string part, string folder) =>
new DirectoryInfo(folder).GetFiles().FirstOrDefault(file => file.Name.Equals(part, StringComparison.OrdinalIgnoreCase))?.Name;
// Searches for a matching folder in the current directory regardless of case
private static string GetDirectoryName(string part, string folder) =>
new DirectoryInfo(folder).GetDirectories().FirstOrDefault(dir => dir.Name.Equals(part, StringComparison.OrdinalIgnoreCase))?.Name;
}
Then in Startup class, make sure you register a provider for content and web root as follow:
_environment.ContentRootFileProvider = new CaseAwarePhysicalFileProvider(_environment.ContentRootPath);
_environment.WebRootFileProvider = new CaseAwarePhysicalFileProvider(_environment.WebRootPath);
It was possible in Windows 7 but not windows 10 and as far as I can tell, it's also not possible on Windows Server at all.
I can only talk about the OS because the Kestrel documentation says:
The URLs for content exposed with UseDirectoryBrowser and UseStaticFiles are subject to the case sensitivity and character restrictions of the underlying file system. For example, Windows is case insensitive—macOS and Linux aren't.
I'd recommend a convention for all filenames ("all lowercase" usually works best). And to check for inconsistencies, you can run a simple PowerShell script that uses regular expressions to check for wrong casing. And that script can be put on a schedule for convenience.
I have the following code which I use frequently with Android and iOS when creating apps. Simply, it creates a lock when inserting, creating or updating a table.
public class SQL
{
readonly object dbLock = new object();
const string DBClauseSyncOff = "PRAGMA SYNCHRONOUS=OFF;";
const string DBClauseVacuum = "VACUUM;";
#region Setup
public bool SetupDB()
{
lock (dbLock)
As soon as the WinPhone 8.1 app hits this lock line, an exception is thrown. As this is part of a Xam.Forms application, I call this into existence using the following
public App()
{
App.Self = this;
var netLanguage = DependencyService.Get<ILocalise>().GetCurrent();
LangResources.Culture = new CultureInfo(netLanguage);
ConnectionString = DependencyService.Get<ISQLite>().GetConnectionString();
App.Self.SQLitePlatform = DependencyService.Get<ISQLite>().GetPlatform();
DBManager = new SQL();
DBManager.SetupDB();
Nothing is being called asynchronously with the two dependency services returning as their names suggest the connection and platform in use.
The calls look like this
public string GetConnectionString()
{
var documents = ApplicationData.Current.LocalFolder.Path;
var pConnectionString = System.IO.Path.Combine(documents, "preferences.db");
var connectionString = string.Format("{0}; New=true; Version=3;PRAGMA locking_mode=NORMAL; PRAGMA journal_mode=WAL; PRAGMA cache_size=20000; PRAGMA page_size=32768; PRAGMA synchronous=off", pConnectionString);
return connectionString;
}
public ISQLitePlatform GetPlatform()
{
return new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT();
}
If I comment out the SetupDB line, the app runs as I would expect. If it is left in, the app crashes with the error that it cannot create the initial display.
Is there something I need to be doing (or not doing) in order for the DB code to work on all platforms and not just Android and iOS?
Where predefined database (.db) should add and how to use it in windows phone 8.1 app?
I am not using Silverlight in my app.
I was trying to do something like this
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
CopyDatabase();
}
private void CopyDatabase()
{
IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication();
String DBFile = "myDB.sqlite";
if (!ISF.FileExists(DBFile)) CopyFromContentToStorage(ISF, "Assets/myDB.sqlite", DBFile);
}
It showing that the namespace name IsolatedStorageFile could not be found.
I found those codes in a sample database app for Windows-phone-8.0 and I was trying to do the same thing in Windows-phone-8.1 (without Silverlight).
As I see you try to copy the database from package to IsolatedStorage and you are targeting WinRT. The sample code can llok like this:
private async Task<bool> CopyDatabase()
{
StorageFolder packageFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFile file = await packageFolder.GetFileAsync("Assets/myDB.sqlite");
await file.CopyAsync(localFolder);
return true;
}
I've written this code from the top of my head, but should work or help you to find the solution. The above is also possible by Uri schemes:
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(#"ms-appx:///Assets/myDB.sqlite"));
More about Data and Files you will find at MSDN.