Xamarin with PCLStorage - Where to place SQLite3 file? - c#

I am reading 'Xamarin in Action' by Manning Publications and working on Countr example. I am using below code to retrieve Sqlite3 file as has been mentioned in chapter 7 of book.
var local = FileSystem.Current.LocalStorage.Path;
var datafile = PortablePath.Combine(local, "counters.db3");
connection = new SQLiteAsyncConnection(datafile);
connection.GetConnection();
I wanted to add one more database which has static content that I already prepared. I need to place this database inside app's assets or resources and read it when app loads. I realized that on Windows, above code is looking for database at "C:\Users\\AppData\Local\Microsoft.TestHost.x86\15.6.2\". For testing Model, ViewModel I placed the database file at that location and that helped. However, I wanted to know what is right place to put database file and how to access it so I can test in Android and iPhone emulators?

Just use Environment.GetFolderPath and store the database the location Android and IOS gives your app to store data without further permissions:
string dbPath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.Personal),
"counters.db3");
You can find more information on this article:
https://developer.xamarin.com/guides/android/data-and-cloud-services/data-access/part-3-using-sqlite-orm/

In Visual Studio, Xamarin Solution by default has multiple projects -
App.Core that has libraries needed for designing Model and ViewModels
App.Droid that has Android SDK libraries to build view for Android devices
App.iOS that has iOS SDK libaries to build View for Apple devices
It is not possible to add below code in App.Core library as that project has no information about App's file system. Only App.Droid and App.iOS projects carry this information.
string dbPath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.Personal),
"counters.db3");
Since we want to copy a database with pre-existing content to LocalDirectory, Setup.cs in App.Droid and App.iOS must be altered. For example, for App.Droid, below are the steps needed (This is one way and there are of course alternatives)
Place Sqlite DB in App.Droid/Assets folder
Create a class file (let's call it AndroidConfiguration.cs) and add a method which I call copyElementDatabaseToLocalStorage() (Code for this method can be found after these list of steps)
In SetUp.cs, call copyElementDatabaseToLocalStorage() method
Code for copyElementDatabaseToLocalStorage Method:
using System;
using Android.App;
using System.IO;
using PCLStorage;
namespace App.Droid.AndroidSpecificSetters
{
public class AndroidConfiguration
public async void copyElementDatabaseToLocalStorage()
{
string localStorage = FileSystem.Current.LocalStorage.Path;
string databaseLocation = Path.Combine(localStorage, "counters.db3");
try
{
if (!File.Exists(databaseLocation))
{
using (var binaryReader = new BinaryReader(Application.Context.Assets.Open("counters.db3")))
{
using (var binaryWriter = new BinaryWriter(new FileStream(databaseLocation, FileMode.Create)))
{
byte[] buffer = new byte[2048];
int length = 0;
while ((length = binaryReader.Read(buffer, 0, buffer.Length)) > 0)
{
binaryWriter.Write(buffer, 0, length);
}
}
}
}
} catch (Exception e)
{
...
}
}
}
}
Code Inside App.Droid Setup.cs constructor
public Setup(Context applicationContext) : base(applicationContext)
{
AndroidConfiguration androidConfiguration = new AndroidConfiguration();
androidConfiguration.copyElementDatabaseToLocalStorage();
}

Related

How can I install Selenium’s webdriver.exe in [PATH] htrough the project’s published installer?

I have a Windows Forms application that uses Selenium. I have multiple production clients that need to run this application and I’ve noticed that in every new client (and also when I need to update the webdriver) I need to copy and paste the .exe to the [PATH] location (%USERPROFILE%\AppData\Local\Microsoft\WindowsApps) and I want to automate that with the setup file that gets generated by Visual Studio every time I publish the application.
I’ve found that you can install an extension called “Microsoft Visual Studio Installer Project”, include the .exe file on it and either make a new Form that’ll check if the webdriver is in place and if it’s not to copy it, or I can change the [PATH] of my IWebDriver object in order to reflect the new path of this file. As a bonus you can also add the the desktop icon.
But first I want to know if there’s a way to publish this webdriver.exe file to it’s proper address through the “Publish wizard” parameters before I start looking for workarounds.
This worked for my use case, for context, I'm using a windows forms project targeting .NET (framework) 4.7.1. these are snippets from my events "load" and "show" formated as a different function. I only included the logic behind the file check, download and unzip with overwite. Since the System.IO.Compression.ZipFile class for this version of .NET doesn't natively support overwrite files, I used Ionic's DotNetZip package downloaded from NuGet.
using Ionic.Zip;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
private void DriverCheck(){
string edge, edgeVersion, edgeDriverPath, edgeDriver, edgeDriverVersion;
edge = #"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe";
edgeVersion = FileVersionInfo.GetVersionInfo(edge).FileVersion;
edgeDriverPath = Environment.GetEnvironmentVariable("LocalAppData") + "\\Microsoft\\WindowsApps\\";
edgeDriver = edgeDriverPath + "msedgedriver.exe";
try
{
edgeDriverVersion = FileVersionInfo.GetVersionInfo(edgeDriver).FileVersion;
}
catch
{
edgeDriverVersion = null;
}
if (!File.Exists(edgeDriver) || edgeVersion != edgeDriverVersion)
{
try
{
using (var client = new WebClient())
{
string winver;
if (Environment.Is64BitProcess)
{
winver = "64";
}
else
{
winver = "32";
}
string zipPath = edgeDriverPath + "edgedriver_win64.zip";
client.DownloadFile("https://msedgedriver.azureedge.net/" + edgeVersion + "/edgedriver_win" + winver + ".zip", zipPath);
using (ZipFile zip = ZipFile.Read(zipPath))
{
foreach (ZipEntry temp in zip)
{
temp.Extract(edgeDriverPath, ExtractExistingFileAction.OverwriteSilently);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error downloading webdriver:\n" + ex.Message);
Application.Exit();
}
}
}

reading a SSIS package variable from it's C# asp launcher

I have to modify an existing SSIS package, to launch it from a web site. Currently, this package is launched on demand by double clicking it, shows a form to ask for an excel file location and some database credentials that are stored in package variables, and then loads data from the excel file into a DB. Since there are many errors that can occur in the process, there is a package variable that holds an internal state, to inform the user which part of the process failed.
Given that I have to launch the package from a web site, as a first approach I have split the package in two, a master package that gets the information from user, executes the slave package by passing the user parameters through package variables, gets the child package internal state and then it finishes by informing the user the final state of this process. The communication between packages is being done by using variables with the same name and package configuration (main package variables). This is true for all variables except for the internal state one, that exists just in the parent, but is used in the child. Since both share the same context, it works ok.
Now that the child package is isolated, I'm trying to replace the master one with a C# asp site. Currently I'm able to get the user parameters through a webform and execute the package, but I can't figure how to read the child's internal state variable from the web app.
This internal value is an integer from 0 to 12, where 0 means ok and any other means that something went wrong with loading a table, executing a SP or something else.
There is a way to get this package variable value from the web app, when the package finishes? Otherwise, I just realized that this could be wrote in a log file that could be read by the web app, but I was wondering if there is a more wise solution.
Just to let you know, this is how I'm passing variables from the web app to the package. The package is configured to set its variables from primary/main package variables.
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SqlServer.Dts.Runtime;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Application app = new Application();
Package package = null;
String PackagePath = "";
try
{
string fileName = FileUpload1.PostedFile.FileName.ToString();
fileName = Server.MapPath("App_Data//" + System.IO.Path.GetFileName(fileName));
FileUpload1.PostedFile.SaveAs(fileName);
//Load DTSX
PackagePath = #"C:\Program Files\Microsoft SQL Server\100\DTS\Packages\Null Project\Package.dtsx";
package = app.LoadPackage(PackagePath, null);
//Global package variables (same name)
Hashtable param = new Hashtable();
param["ServidorOrigen"] = "SQL_SERVER";
param["UserOrigen"] = "user";
param["PassOrigen"] = "pass";
param["BaseDatosOrigen"] = "test_database";
param["EstadoConexion"] = 0;
param["EstadoPaquete"] = 0;
param["ExcelRuta"] = fileName.ToString();
Variables vars = null;
foreach (DictionaryEntry entry in param)
{
package.VariableDispenser.LockOneForWrite(entry.Key.ToString(), ref vars);
try
{
vars[entry.Key.ToString()].Value = entry.Value.ToString();
}
catch
{
throw new Exception("variable " + entry.Key.ToString() + " not found in package");
}
finally
{
vars.Unlock();
}
}
//Execute DTSX
Microsoft.SqlServer.Dts.Runtime.DTSExecResult results = package.Execute();
//Collects debugging info
using (StreamWriter _testData = new StreamWriter(Server.MapPath("~/App_Data/log.txt"), true))
{
if (!package.Errors.Count.Equals(0)){
_testData.WriteLine(package.Errors.Count.ToString()); // Write the file.
ErrorEnumerator myEnumerator = package.Errors.GetEnumerator();
int i = 0;
while ((myEnumerator.MoveNext()) && (myEnumerator.Current != null))
_testData.WriteLine("[{0}] {1}", i++, myEnumerator.Current.Description);
}
}
}
catch (Exception ex)
{
throw ex;
}
}
}
You can store the state in a database table the same way you would with your log file and then just have your web app read that at a given interval.
I'm not sure how you are passing your variables from the web app to the ssis, but you could look into the ssis configuration stuff storing in sql databases.
I have a similar thing I do.
Config stuff saved to database from web app.
Web app calls a sql job.
Job starts ssis package.
Web app queries every minute to see if the job has finished and returns succeeded or failed to user.

Create solution file(.sln) and project files(.csproj) dynamically

I am creating code generation tool (auto code generation based on table structure) as a Windows forms application in Visual Studio 2012 using .NET Framework 4.0. It's generating the portable object, controller, WCF services and business logic code files.
All code files bundle in the appropriate project and all project bundle in one solution. The solution and projects need to create dynamically through program.
I have tried to create the solution and project using Visual Studio Add-in project. It is working fine in Add-In project (separate solution). The OnConnection method call automatically in Add-in project. Now I want to implements this in my code generation tool. While debugging in Add-In project the application variable shown like COM object.
I am tried to pass the value for OnConnection method from code generation tool, it throws an error (I passed this object for application variable). I really don't know how to call this method from my code generation tool. Anyone help this?
Code
private DTE2 _applicationObject;
private AddIn _addInInstance;
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
createProjectsFromTemplates(_applicationObject);
}
public void createProjectsFromTemplates(DTE2 dte)
{
try
{
Solution2 soln = (Solution2)dte.Solution;
string csTemplatePath;
string csPrjPath = "SamplePath\\TestCreateProject";
csTemplatePath = soln.GetProjectTemplate("WpfApplication.zip", "CSharp");
System.Windows.Forms.MessageBox.Show("C# template path: " + csTemplatePath);
soln.AddFromTemplate(csTemplatePath, csPrjPath, "NewWCFCSharpAutoGeneratorProject", false);
Project prj;
ProjectItem prjItem;
String itemPath;
// Point to the first project (the Visual Basic project).
prj = soln.Projects.Item(1);
prjItem = prj.ProjectItems.AddFromFileCopy("SampelCSharp.cs");
}
catch (System.Exception ex)
{
System.Windows.Forms.MessageBox.Show("ERROR: " + ex.Message);
}
}
You can instantiate a VS from the host application and generate the files. Hope that will work. The below code works well for me.
Use the following namespaces to get work the below given code.
Namespaces:
using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.CommandBars;
using System.Resources;
using System.Reflection;
Code:
EnvDTE80.DTE2 dte2;
dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.11.0");
Connect objConnect = new Connect();
Array objArray = null;
objConnect.OnConnection(dte2, ext_ConnectMode.ext_cm_UISetup, null, ref objArray);
I got this reference it is really useful.
http://rcos.rpi.edu/projects/unmake/commit/programmatically-launch-devenv-generate-a-solution-and-save-it/
You can use this. This is for .cs project files and framewwork above .NET 2.0 versions. VB project sources are not compatible.
protected void Build(string project)
{
Engine engine = new Engine();
BuildPropertyGroup properties = new BuildPropertyGroup();
properties.SetProperty(#"Configuration", #"Debug");
// Point to the path that contains the .NET Framework 2.0 CLR and tools
engine.BinPath = #"c:\windows\microsoft.net\framework\v3.5";
// Instantiate a new FileLogger to generate build log
FileLogger logger = new FileLogger();
// Set the logfile parameter to indicate the log destination
string str = #"logfile=D:\temp";
str += project.Substring(project.LastIndexOf("\\"), project.LastIndexOf(".") - project.LastIndexOf("\\")) + ".log";
logger.Parameters = str;
// Register the logger with the engine
engine.RegisterLogger(logger);
// Build a project file
bool success = engine.BuildProjectFile(project, new string[] { "build" }, properties);
//Unregister all loggers to close the log file
engine.UnregisterAllLoggers();
using (BinaryWriter writer = new BinaryWriter(File.Open(#"D:\temp\Prj.log", FileMode.Append)))
{
if (success)
{
writer.Write("\nBuild Success :" + project.Substring(project.LastIndexOf("\\")));
}
else
{
writer.Write("\nBuild Fail :" + project.Substring(project.LastIndexOf("\\")));
}
}
}

BundleTransformer.Less inject variables depending on context/request

We would like the use the bundling mechanism of System.Web.Optimization in combination with the Less transformer.
The problem is that the same application/server serves pages for different branded websites. So depending on the 'SiteContext' the same .less files are used but different values should be used by the .less variables. So we want the (re)use the same less files but with different variables depending on the context of the request.
I tried a couple of different theories:
In all 3 cases I setup different bundles depending on the SiteContext.
1 inject an #import directive with the themed variables by using a custom VirtualPathProvider that intercepts the variables.less file.
So I have:
the styling file eg: header.less (imports the variables file)
the variables file: variables.less
a themed variables file: variables-theme.less (injected in variables.less via the VirtualPathProvider)
This is not working because the BundleTransformer cache sees this as the same file and doesn't know about the SiteContext. The cache key is based on the Url of the IAsset and we cannot influence this behavior.
2 Replace the variables.less import by variables-themed.less with an custom transformer that runs before the Less transformer.
Again no luck, same caching issues.
And as a side effect, the extra transformer was not called in debug because the assets are not bundled but called individually by the LessAssetHandler. This could be solved by writing your own AssetHandler that calls all required transformers.
3 create themed Asset names that are resolved by a custom VirtualPathProvider
Eg. Add header-themeX.less to the bundle, this file doesn't exist but you resolve this file to header.less and use method 2 to set the correct variables file import. (replace the import of the variables.less to the themed version).
Once again no luck. I think this could solve the caching issue if it wasn't for the Bundle.Include(string virtualPath) that does a File.Exists(path) internally. It doesn't pass via the CustomVirtualPathProvider.
Am I looking to deep to solve this?
All ideas are welcome, I can imagine that this will become a problem to more and more people as the System.Web.Optimization library gets more popular...
Keep in mind that:
we have a lot of .less/css files
we will have 5 or so themes
we like to keep things working in visual studio (that is why header.less has a ref. to variables.less)
Thanks for any feedback.
Michael!
You use the Microsoft ASP.NET Web Optimization Framework and the Bundle Transformer in multi-tenant environment, so you need to replace some components of the System.Web.Optimization and create own versions of the debugging HTTP-handlers (see «Problem: LESS file imports are added to BundleResponse.Files collection» discussion). As far as I know, Murat Cakir solve all these problems in the SmartStore.NET project.
In the Bundle Transformer there are 2 ways to inject of LESS-variables:
Look a properties GlobalVariables and ModifyVariables of LESS-translator:
using System.Collections.Generic;
using System.Web.Optimization;
using BundleTransformer.Core.Builders;
using BundleTransformer.Core.Orderers;
using BundleTransformer.Core.Transformers;
using BundleTransformer.Core.Translators;
using BundleTransformer.Less.Translators;
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
var nullBuilder = new NullBuilder();
var nullOrderer = new NullOrderer();
var lessTranslator = new LessTranslator
{
GlobalVariables = "my-variable='Hurrah!'",
ModifyVariables = "font-family-base='Comic Sans MS';body-bg=lime;font-size-h1=50px"
};
var cssTransformer = new CssTransformer(new List<ITranslator>{ lessTranslator });
var commonStylesBundle = new Bundle("~/Bundles/BootstrapStyles");
commonStylesBundle.Include(
"~/Content/less/bootstrap-3.1.1/bootstrap.less");
commonStylesBundle.Builder = nullBuilder;
commonStylesBundle.Transforms.Add(cssTransformer);
commonStylesBundle.Orderer = nullOrderer;
bundles.Add(commonStylesBundle);
}
}
Create a custom item transformation:
using System.Text;
using System.Web.Optimization;
public sealed class InjectContentItemTransform : IItemTransform
{
private readonly string _beforeContent;
private readonly string _afterContent;
public InjectContentItemTransform(string beforeContent, string afterContent)
{
_beforeContent = beforeContent ?? string.Empty;
_afterContent = afterContent ?? string.Empty;
}
public string Process(string includedVirtualPath, string input)
{
if (_beforeContent.Length == 0 && _afterContent.Length == 0)
{
return input;
}
var contentBuilder = new StringBuilder();
if (_beforeContent.Length > 0)
{
contentBuilder.AppendLine(_beforeContent);
}
contentBuilder.AppendLine(input);
if (_afterContent.Length > 0)
{
contentBuilder.AppendLine(_afterContent);
}
return contentBuilder.ToString();
}
}
And register this transformation as follows:
using System.Web.Optimization;
using BundleTransformer.Core.Orderers;
using BundleTransformer.Core.Bundles;
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
var nullOrderer = new NullOrderer();
const string beforeLessCodeToInject = #"#my-variable: 'Hurrah!';";
const string afterLessCodeToInject = #"#font-family-base: 'Comic Sans MS';
#body-bg: lime;
#font-size-h1: 50px;";
var commonStylesBundle = new CustomStyleBundle("~/Bundles/BootstrapStyles");
commonStylesBundle.Include(
"~/Content/less/bootstrap-3.1.1/bootstrap.less",
new InjectContentItemTransform(beforeLessCodeToInject, afterLessCodeToInject));
commonStylesBundle.Orderer = nullOrderer;
bundles.Add(commonStylesBundle);
}
}
Both ways have disadvantage: the injection of LESS-variables does not work in debug mode.

FileVersionInfo.GetVersionInfo() incorrect in Console Application

I'm getting some serious weirdness using FileVersionInfo.GetVersionInfo() and was hoping somebody might be able to help.
The basics of the issue is that I am iterating through all the files in a folder calling GetVersionInfo() on each. There are about 300 files. This works ok for all but 2 of the files. For these DLLs I am getting comepletely incorrect info back from GetVersionInfo().
In order to eliminate all other variables, I extracted this call into a simple test app and it still got the same problem. However, if I built the test app as a Windows Application (it was a Console Application initially) then the data came back correct.
Just to clarify, the incorrect data coming back when running as a Console App is not simply null info like you would get if the file didn't contain version data. It contained reasonable data, but just the wrong data. It's as if it's reading it from a different file. I've looked for a file that contains matching version data, but can't find one.
Why is this simple call functioning differently if built as a Console Application rather than a Windows Application?
If anyone can help with this I would be very grateful.
Rgds,
Andy
-- Code Added
using System;
using System.Diagnostics;
namespace test
{
class Program
{
static void Main(string[] args)
{
string file = "C:\\ProblemFile.dll";
FileVersionInfo version = FileVersionInfo.GetVersionInfo(file);
string fileName = version.FileName;
string fileVersion = version.FileVersion;
Console.WriteLine(string.Format("{0} : {1}", fileName, fileVersion));
}
}
}
This behaviour seems weird indeed. Could it be that the Console application does not load the DLL from the same place as the WinForms application does? This would mean that GetVersionInfo uses some other API than just Win32 CreateFile (maybe going through some DLL resolver mechanism, side-by-side or whatever); remember that under the covers, version.dll will be executing your request, not the CLR itself.
Looking at FileVersionInfo through Reflector points in another direction yet:
public static unsafe FileVersionInfo GetVersionInfo(string fileName)
{
// ...
int fileVersionInfoSize = UnsafeNativeMethods.GetFileVersionInfoSize(fileName, out num);
FileVersionInfo info = new FileVersionInfo(fileName);
if (fileVersionInfoSize != 0)
{
byte[] buffer = new byte[fileVersionInfoSize];
fixed (byte* numRef = buffer)
{
IntPtr handle = new IntPtr((void*) numRef);
if (!UnsafeNativeMethods.GetFileVersionInfo(fileName, 0, fileVersionInfoSize, new HandleRef(null, handle)))
{
return info;
}
int varEntry = GetVarEntry(handle);
if (!info.GetVersionInfoForCodePage(handle, ConvertTo8DigitHex(varEntry)))
{
int[] numArray = new int[] { 0x40904b0, 0x40904e4, 0x4090000 };
foreach (int num4 in numArray)
{
if ((num4 != varEntry) && info.GetVersionInfoForCodePage(handle, ConvertTo8DigitHex(num4)))
{
return info;
}
}
}
}
}
return info;
}
As you can see there, some interesting dance is going on with code pages. What if the DLLs you inspected had several version information resources attached to them? Depending on the culture of the program calling into GetVersionInfo, I guess that the code page related calls could return other results?
Take the time to check the resources of the DLLs and make sure that there is only one language/code page for the version information. It might point you at the solution, I hope.
Sure the "files" you're seeing aren't . and .. ? If you iterate through all files, you'll always see entries for . (current dir) and .. (up dir). GetVersion Info might well return anything for these. You'd have to filter these entries out manually by name.
File and Assembly versions are 2 different things.
Are you sure you are not expecting the other?
Update: Tried this. Didn't work.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace test
{
class Program
{
[DllImport("COMCTL32")]
private static extern int InitCommonControls(int nExitCode);
static void Main(string[] args)
{
InitCommonControls(0);
string file = "C:\\ProblemFile.dll";
FileVersionInfo version = FileVersionInfo.GetVersionInfo(file);
string fileName = version.FileName;
string fileVersion = version.FileVersion;
Console.WriteLine(string.Format("{0} : {1}", fileName, fileVersion));
}
}
}

Categories