This is my first post, but I have been scouring the stack for how I might use a text file or a SQLite database in Xamarin for Visual Studio. All the examples I have found end up in java, or in the old Mono android code.
The jist of the application is to read GPS data, display it, and save the latitude and longitude along with some identifying info such as room number. I have the display portion of the app working nicely, so now I need to find a way to save the information, so that the data can be passed along to a future class to finish creating a campus navigation app.
Here are some of the related portions:
path = GetDir("MCCMapping/",FileCreationMode.Append).ToString();
I am not sure this is correct to set the directory for the file creation.
String location = String.Format("{0}|{1}|{2}|{3}", roomNum.Text, campus.SelectedItem.ToString(), current.Latitude.ToString(),
current.Longitude.ToString());
using (data_file = new StreamWriter(path+"/MCC_ROOM_DATA.txt", true))
{
data_file.WriteLine(location);
}
This is not quite working. I need to be able to access the .txt file from outside the application, or even implement a SQLite database to store the same information. I have all the necessary manifest permissions. My device does not have external storage, so it will emulate it if that is the route that must be taken.
Anything you might have to help me along would be appreciated. Thanks.
Update 1:
folder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
conn = new SQLiteAsyncConnection(System.IO.Path.Combine(folder, "MCC_ROOMS.db"));
conn.CreateTableAsync<Room>().ContinueWith(t => {});
and this
public class Room
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Campus { get; set; }
[Unique]
public string Room_Num { get; set; }
[Unique]
public decimal Room_Latitude { get; set; }
[Unique]
public decimal Room_Longitude { get; set; }
}
I am not quite sure what to put in the continue with portion. Also, would this allow me to access the table from a different application later? Thanks.
Update 2: Thanks to Jason, I have a working application. I used the external storage directory that is built in, and can then access the file if I turn usb debugging off.
Xamarin provides enumerations that help you get the folder paths of the folders that you are allowed to access. You cannot just write to arbitrary file paths.
var path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var filename = Path.Combine(path, "mydata.txt");
Once you have the path, you can use normal file IO operations to read and write.
using (data_file = new StreamWriter(filename, true))
{
data_file.WriteLine(location);
}
Xamarin's SQLite.Net component has documentation that demonstrates how to create and utilize a db from within your app.
string folder = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
var conn = new SQLiteConnection (System.IO.Path.Combine (folder, "stocks.db"));
Related
I'm having an issue using the Windows credential manager in a project. I am using it to replace the username and password on the connectionString in my appsettings, and in the development and QA environments everything works fine, but in the production environment (which I don't have complete access to) it does not. The issue is its returning empty string when I load the credentials from the target.
Here is where I am loading it:
public static CredentialModel GetCredential(string target)
{
CredentialModel credentialDto = new CredentialModel();
using var credential = new Credential
{
Target = target
};
credential.Load();
credentialDto.UserName = credential.Username;
credentialDto.Password = credential.Password;
return credentialDto;
}
And this is the CredentialModel
public class CredentialModel
{
public string UserName { get; set; }
public string Password { get; set; }
}
And where I replace the credentials in the connectionString:
StringBuilder connectionString = new(host.Configuration.GetConnectionString("RemessasConnectionString"));
var credential = CredentialService.GetCredential("Pegasus");
connectionString.Replace("$userId", credential.UserName);
connectionString.Replace("$password", credential.Password);
ConnectionString = connectionString.ToString();
For debugging's sake I added a line to the log in order to see what was being added to the connectionString, and it is replacing it with an empty string in production, but the actual values in development.
I have one idea about the reason for this, the application is running with a windows user and that user does not have access to the windows credential manager in the production server (but I think this would return an error not just empty strings).
If anyone can point me in the right direction, or has any suggestions for me to try I am all ears.
As #richard-deeming pointed out, the its because the user running the application does not have access to the credentials stored since they were stored under a different account than the one running the service. Look at his comment for more detail.
I'm creating a game using C# and trying to incorporate a CSV for parsing previous scores into a leaderboard and also writing to the file when a player finishes their game.
This is the data stored relating to a score
If this was a sole project I would store the csv in the bin > Debug folder and pass the file path to a StreamReader. Although, this is a group project using Azure Devops/TFS as source control so I'm not too sure what way is best to do this.
I have tried storing the CSV in the Resources of the project but I didn't realise this embeds the file in the project and only allows for reading from the file.
The CSV is currently read like:
var file = Properties.Resources.highscores;
char[] splitter = "\r\n".ToCharArray();
string[] scoresCsv = Properties.Resources.highscores.Split(splitter);
foreach (string score in scoresCsv)
{
if(!String.IsNullOrEmpty(score))
{
var values = score.Split(',');
highScores.Add(new HighScore(values[0], Convert.ToInt32(values[1]), Convert.ToDateTime(values[2])));
}
}
this.highScores = highScores.OrderByDescending(x => x.Score).ToList();
Select the "Team Explorer" window and go to "Source Control Explorer"
Here you will see a global view of the project.
You can add files to your project in any folder you wish outside of the actual source. If you want to you can add your bin folder into the source control and keep that file in the bin folder.
Where-ever you put the file you just need to know the location to it from your project and you are able to map to it and edit it in runtime.
Another option is to create a folder in the C:\ProgramData folder for your game and you can write the leaderboards directly into their C drive when they run the game. People would be able to modify the leaderboards but, obviously the game is for learning purposes of coding and usually you wouldn't store the leaderboards on the client side anyway it would be on a server.
This assumes that the high score data is not shared, and stores it locally. It doesn't require the file to be added to source control.
public class ScoreFileHandler
{
private static string appPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "YourAppName");
private static string scoreFileName = "highscores.txt";
private static string filePath = Path.Combine(appPath, scoreFileName);
public string ReadFile()
{
if (!Directory.Exists(appPath))
{
Directory.CreateDirectory(appPath);
}
if (File.Exists(filePath))
{
return File.ReadAllText(filePath);
}
return string.Empty; // TODO - caller needs to handle this
}
public void WriteFile(string csvScoreData)
{
if (!Directory.Exists(appPath))
{
Directory.CreateDirectory(appPath);
}
File.WriteAllText(filePath, csvScoreData);
}
}
i don't seem to be able to read anything from this file in an azure functions when running or debugging a test, however it works fine when debugging the whole application locally.. can anyone explain why at all ?
{
"IsEncrypted": false,
"Values": {
"xyz": 123
}
}
var res = ConfigurationManager.AppSettings.Get("xyz");
ty..
my suspicion is that it is due to the 'debug' being initiated from another project (Test project), and the local.settings.json does not get bundled up with the project being tested ?
I added the settings programatically to minify the chances that sensitive data reach version control.
class LocalSettings
{
public bool IsEncrypted { get; set; }
public Dictionary<string, string> Values { get; set; }
}
public static void SetupEnvironment()
{
string basePath = Path.GetFullPath(#"..\..\..\MyAzureFunc");
var settings = JsonConvert.DeserializeObject<LocalSettings>(
File.ReadAllText(basePath + "\\local.settings.json"));
foreach (var setting in settings.Values)
{
Environment.SetEnvironmentVariable(setting.Key, setting.Value);
}
}
Your suspicion is spot on. Only the Azure Functions Runtime Host actually knows to read settings from that file and merge them into the overall AppSettings. When you run in a test project, the Azure Functions Runtime Host is not involved and therefore you don't get access to them.
The simplest way to solve this would be to reflect all the same setting keys/values into your test project's app.config file under the standard <appSettings> section.
I'm wondering if the Portable Class Library is even more restricted in functionality than the Compact Framework.
I'm trying to port a CF/Windows CE app (runs on a handheld device) to a Xamarin solution that will target Android, iOS, Windows Phone, and perhaps other things.
One of the problems I run into, though, is that this legacy code (which works under CF):
public static List<string> GetXMLFiles(string fileType, string startingDir)
{
const string EXTENSION = ".XML";
string dirName = startingDir;
// call it like so: GetXMLFiles("ABC", "\\"); <= I think the double-whack is what I need for Windows CE device...am I right?
var fileNames = new List<String>();
try
{
foreach (string f in Directory.GetFiles(dirName))
{
string extension = Path.GetExtension(f);
if (extension != null)
{
string ext = extension.ToUpper();
string fileNameOnly = Path.GetFileNameWithoutExtension(f);
if (fileNameOnly != null &&
((ext.Equals(EXTENSION, StringComparison.OrdinalIgnoreCase)) &&
(fileNameOnly.Contains(fileType))))
{
fileNames.Add(f);
}
}
}
foreach (string d in Directory.GetDirectories(dirName))
{
fileNames.AddRange(GetXMLFiles(fileType, d));
// from Brad Rem's answer here: http://stackoverflow.com/questions/22186198/why-is-this-function-returning-nothing-although-there-is-a-match/22186351?noredirect=1#22186351
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return fileNames;
}
...won't compile in the Xamarin/CPL solution. I get, "The name 'Directory' does not exist in the current context" and right-clicking that word does not afford a "resolve" option.
Is there a way to get PCL to recognize "Directory" or must I completely rewrite the code? If the latter, does anybody have any suggestions on what to do/use in its stead?
Relatedly, is there an URL that will show me what is [not] available in PCL and/or a site that will show how much of a provided block of code is "PCL-ready"?
UPDATE
The first image in this article is very illuminating. Later on, it specifically talks about "Directory" not being available in the PCL scenario.
UPDATE 2
I downloaded the PCLStorage package referenced by Daniel Plaisted below to allow me to access the file system within a PCL project.
Using the sample code at the start of the download page [http://pclstorage.codeplex.com/] as a starting point, I've gotten this far:
public async Task<List<string>> GetXMLFiles(string fileType, string startingDir)
{
const string EXTENSION = ".XML";
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder folder = await rootFolder.GetFolderAsync(startingDir, CreationCollisionOption.OpenIfExists); //CreateFolderAsync(startingDir, CreationCollisionOption.OpenIfExists);
List<string> fileNames = await folder.GetFilesAsync(EXTENSION);
return fileNames;
}
...but "EXTENSION" as the arg to GetFilesAsync() is not right. I get with this, "Argument 1: cannot convert from 'string' to 'System.Threading.CancellationToken'"
So what need I do to get all the *.XML files the folder?
UPDATE 3
This compiles, but I'm not at all sure it's the right way to do it, besides the fact that it simply gets all the files from the folder, rather than just those that match "*.XML":
public async Task<List<IFile>> GetXMLFiles(string fileType, string startingDir)
{
const string EXTENSION = ".XML";
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder folder = await rootFolder.GetFolderAsync(startingDir, System.Threading.CancellationToken.None);
IList<PCLStorage.IFile> fileNames = await folder.GetFilesAsync(System.Threading.CancellationToken.None);
return fileNames.ToList();
}
Since in a PCL I was unable to get a StreamWriter from a string (it required a stream), I created a simple interface to get some of the data from the platform implementation. You can also do this with DirectoryInfo and FileInfo.
https://github.com/sami1971/SimplyMobile/blob/master/Core/SimplyMobile.Text/IStreamLocator.cs
The implementation is really simple as well, only needs one single compiler flag for WP8:
https://github.com/sami1971/SimplyMobile/blob/master/WP8/SimplyMobile.Text.Platform/StreamLocator.cs
Recursively search for *.XML files:
private static void PrintDirectory(IStreamLocator locator, string dir)
{
foreach (var file in locator.GetFileNames(dir, "*.XML"))
{
System.Diagnostics.Debug.WriteLine(file);
}
foreach (var di in locator.GetFolderNames(dir, "*"))
{
PrintDirectory(locator, di);
}
}
Windows Phone applications do not use the file system of the operating
system and are restricted to using isolated storage to persist and
access files, so this namespace does not provide any additional
functionality.
http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.io%28v=vs.105%29.aspx
Xamarin has a scanner which will give you a rough idea of the portability of your code: http://scan.xamarin.com/
For some guidance on how to deal with non-portable APIs from PCLs, see my blog post: How to Make Portable Class Libraries Work for You
For file IO in particular, you can try my PCL Storage library.
Another option is to use Shim if all your platforms are supported by it.
API coverage for file operations isn't exhaustive, but it gets you a long way. As a bonus, it also gives you access to a bunch of other stuff.
i have a windows phone application and am trying to connect to a database. The database is valid, i have verified. I am using codeplex's sqlite client to try to connect.
First i add my database to my project using ">add>Existing item>" and after that i try to connect using the code :
db = new SQLiteConnection(#"Database.db");
db.Open();
Debug.WriteLine("DB opened");
SQLiteCommand cmd = db.CreateCommand("SELECT * FROM Tags");
var lst = cmd.ExecuteQuery<Tags>();
foreach (Tags r in lst)
{
Debug.WriteLine(r.Tag);
}
Debug.WriteLine(":D");
My Tags class looks like this :
public class Tags
{
public int id { get; set; }
public string Tag { get; set; }
}
And i get an error in the sqlite project ( in Sqlite3.Vdbe Prepare()) saying that there is no such table:Tags . The table is there, i've viewed the database and the name of the table is correct.
What am i doing wrong?
Try supplying the full path instead of just the file name. I believe windows phone does not have a "current directory" so the application probably looks in the root folder.
If you supply a file that does not exist to the SQLite client, it will silently create an empty database for you and "connect" to that.
i've found that my database must be in the IsolatedStorage. Here is a sample i found useful http://dotnetslackers.com/articles/silverlight/Windows-Phone-7-Native-Database-Programming-via-Sqlite-Client-for-Windows-Phone.aspx