I have a Windows Phone 8.1 Class Library that I want to later add as a reference to a Windows Phone 8.1 App project.
This ClassLibrary should be responsible for creating and managing its own database. I tried creating a new SQLiteConnection in my ClassLibrary, but it throws the following error: A first chance exception of type 'System.InvalidOperationException' occurred in SQLitePCL.DLL however, if I do the same in my MainApp everything works fine.
So, is it possible to create a SQLite database in a ClassLibrary that's responsible for creating and managing it without any support from the MainApp.
I have a project in it where the SQLite library is in a class library and then I use another class library for the communication between my app and the SQLite library
Class library: SQLite.Library
Make a new class library (in my case I named it SQLite.Library)
Right click > Manage NuGet packages > sqlite-net (https://www.nuget.org/packages/sqlite-net/1.0.8)
After adding this NuGet package you see that your class library has 2 new classes: SQLite.cs and SQLiteAsync.cs.
Also there is a known problem with SQLite and threading (NullReferenceException when page Loads), you can fix it by adding a lock in the method TableMapping GetMapping in SQLite.cs:
public TableMapping GetMapping(Type type, CreateFlags createFlags = CreateFlags.None)
{
if (_mappings == null) {
_mappings = new Dictionary<string, TableMapping> ();
}
lock (_mappings)
{
TableMapping map;
if (!_mappings.TryGetValue(type.FullName, out map))
{
map = new TableMapping(type, createFlags);
_mappings[type.FullName] = map;
}
return map;
}
}
Class library: Solutionname.Lib
Make a new class library (in my case I named it Solutionname.Lib)
Right click > Add Reference > Solution > SQLite.Library (the class library u just made)
After the reference is set u can use the SQLite library in this class library.
In my project I tried to split my code a bit so I started with making a class named DatabaseHelper.cs:
public class DatabaseHelper
{
private String DB_NAME = "DATABASENAME.db";
public SQLiteAsyncConnection Conn { get; set; }
public DatabaseHelper()
{
Conn = new SQLiteAsyncConnection(DB_NAME);
this.InitDb();
}
public async void InitDb()
{
// Create Db if not exist
bool dbExist = await CheckDbAsync();
if (!dbExist)
{
await CreateDatabaseAsync();
}
}
public async Task<bool> CheckDbAsync()
{
bool dbExist = true;
try
{
StorageFile sf = await ApplicationData.Current.LocalFolder.GetFileAsync(DB_NAME);
}
catch (Exception)
{
dbExist = false;
}
return dbExist;
}
private async Task CreateDatabaseAsync()
{
//add tables here
//example: await Conn.CreateTableAsync<DbComment>();
}
}
After the creation of the DatabaseHelper class u can start by making a datasource class for each table in your database.
In my case i have a CommentDataSource.cs:
public class CommentDataSource
{
private DatabaseHelper db;
public CommentDataSource(DatabaseHelper databaseHelper)
{
this.db = databaseHelper;
}
public async Task<long> AddComment(String vat, String comment)
{
long id = 0;
DateTime date = DateTime.Now;
DbComment dbc = new DbComment(vat, comment, date);
await db.Conn.InsertAsync(dbc);
DbComment insertDbc = await db.Conn.Table<DbComment>().ElementAtAsync(await db.Conn.Table<DbComment>().CountAsync() - 1);
if (insertDbc != null)
{
id = insertDbc.Id;
}
return id;
}
public async void RemoveComment(long idComment)
{
DbComment comment = await db.Conn.Table<DbComment>().Where(c => c.Id == idComment).FirstOrDefaultAsync();
if (comment != null)
{
await db.Conn.DeleteAsync(comment);
}
}
public async Task<List<DbComment>> FetchAllComments(String vat)
{
return await db.Conn.Table<DbComment>().Where(x => x.VAT == vat).ToListAsync();
}
}
As you can see all the datasources that u will add will make use of the same databasehelper.
Use the Solutionname.Lib in your app
Right click > Add Reference > Solution > SQLite.Library (the class library u just made)
Right click > Add Reference > Solution > Solutionname.Lib
You still need to add a reference to your sqlite lib otherwise you will get errors.
Now you can start using your datasource classes, like u can see here:
private DatabaseHelper db = new DatabaseHelper();
private CommentDataSource commentDataSource;
public MainPage()
{
this.InitializeComponent();
commentDataSource = new CommentDataSource(db);
}
Now is every method of the CommentsDataSource available in your app.
Hope this help u a bit!
try this
public async Task<bool> CheckDbAsync(string dbName)
{
bool dbExist = true;
try
{
StorageFile sf = await ApplicationData.Current.LocalFolder.GetFileAsync(dbName);
}
catch (Exception)
{
dbExist = false;
}
return dbExist;
}
public async Task CreateDatabaseAsync(string dbName)
{
SQLiteAsyncConnection con = new SQLiteAsyncConnection(dbName);
await con.CreateTableAsync<ChatClass>();
// await con.CreateTableAsync<RecentChatManageClass>();
await con.CreateTableAsync<PurchasedGift>();
// await con.CreateTableAsync<AttandanceManagement>();
}
and use like this
DataBaseOperation databaseoperation = new DataBaseOperation();
bool existDb = await databaseoperation.CheckDbAsync("sample.db"); // Check Database created or not
if (!existDb)
{
await databaseoperation.CreateDatabaseAsync("sample.db"); // Create Database
}
Related
I'm working on a Xamarin mobile app using .NET Framework, and SQLite.NET-PCL. My XAML Form uses MVVM architecture, on my main form I am attempting to display all movies in a ListView, first I made sure the view itself works correctly, and now I am attempting to connect it to my DB service. When the DB service initializes and attempts to create any table using CreateTableAsyc() the program gets stuck in that function even after appearing to succeed.
Initially, I thought it was due to using created classes as properties, which SQLite does not support, so I corrected that. Next, I checked if it was due to not using Hardware Acceleration so I enabled it. I then attempted to debug using ContinueWith() and printed the result to the debug console, it says 'Migrated' but never exits the function. Xamarin XAML Hot Reload then times out. What could be causing this?
Example of one of the Types:
using SQLite;
public class Movie
{
public Movie() {}
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Title { get; set; }
public DateTime Length { get; set; }
}
DBService
using Xamarin.Essentials;
public static class DBService
{
private static SQLiteAsyncConnection dbConn;
public static async Task Init()
{
if (dbConn != null)
{
return;
}
string databasePath = Path.Combine(FileSystem.AppDataDirectory, "Movies.db");
dbConn = new SQLiteAsyncConnection(databasePath, false);
await dbConn.CreateTableAsync<Movie>().ContinueWith(results =>
{
Debug.WriteLine(results.Result.ToString()); // This prints migrated
});
Debug.WriteLine("After table created"); // This never prints
}
public static async Task<IEnumerable<Movie>> GetMovies()
{
await Init();
return await dbConn.Table<Movie>().ToListAsync();
}
}
MovieViewModel (View Model for main view)
public class MovieViewModel
{
public List<Movie> Movies { get; set; }
public MovieViewModel ()
{
Movies = (List<Movie>)DBService.GetMovies().Result;
}
}
Wrap awaitable in something that runs after constructor returns:
MainThread.BeginInvoke(async () => Movies = (await DBService.GetMovies()).ToList() );
Optional:
GetMovies could return a List, to match Movies declaration. Then would not need .ToList(), or (List<Movie>) cast:
public static async Task<List<Movie>> GetMovies() { ... }
MainThread.BeginInvoke(async () => Movies = await DBService.GetMovies() );
I am making a test VisualStudio extension that adds new items to solution. I found the way of doing it through trial and error, but I am not satisfied with the complexity of this solution and have some opened questions.. Here is the code:
using EnvDTE;
using EnvDTE80;
//..
namespace MyFirstExtension
{
internal sealed class AddFileToSolution
{
public const int CommandId = PackageIds.CmdCreateVaultHelperConfig;
public static readonly Guid CommandSet = new Guid(PackageGuids.guidVaultHelperVsExtensionPackageCmdSetString);
private readonly AsyncPackage package;
private AddFileToSolution(AsyncPackage package, OleMenuCommandService commandService)
{
this.package = package ?? throw new ArgumentNullException(nameof(package));
commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
var menuCommandID = new CommandID(CommandSet, CommandId);
var menuItem = new MenuCommand(this.Execute, menuCommandID);
commandService.AddCommand(menuItem);
}
public static AddFileToSolution Instance { get; private set; }
private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider { get { return this.package; } }
public static async Task InitializeAsync(AsyncPackage package)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
Instance = new AddFileToSolution(package, commandService);
}
private async void Execute(object sender, EventArgs e)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
string fileFullPath = #"C:\Users\myusr\source\repos\VsExtensionTesting2\0.txt";
var dte = await package.GetServiceAsync(typeof(DTE));
var dte2 = dte as DTE2;
var solution2 = (Solution2)dte2.Solution;
// following line throws Exception!
//solution2.AddFromFile(fileFullPath);
bool isSolutionItemsFolderExists = IsSolutionItemsFolderExists(solution2);
if (!isSolutionItemsFolderExists)
{
solution2.AddSolutionFolder("Solution Items");
}
AddToSolutionItems(solution2, fileFullPath);
}
public bool IsSolutionItemsFolderExists(Solution2 solution2)
{
foreach (var solutionItemObj in solution2)
{
var solutionItem = solutionItemObj as Project;
string name = solutionItem.Name;
if (name == "Solution Items")
{
return true;
}
}
return false;
}
public void AddToSolutionItems(Solution2 solution2, string fileFullPath)
{
foreach (var solutionItemObj in solution2)
{
var solutionItem = solutionItemObj as Project;
string name = solutionItem.Name;
if (name == "Solution Items")
{
solutionItem.ProjectItems.AddFromFile(fileFullPath);
}
}
}
}
}
Here I am using the Visual Studio 2019 VSIX project template. It uses AsyncPackage by default. What I am trying to accomplish is to execute command Project.AddExistingItem on solution level.
Questions are:
In my solution - why does calling the command solution2.AddFromFile(fileFullPath); directly throws exception?
System.Runtime.InteropServices.COMException: 'The template specified cannot be found. Please check that the full path is correct.'
The file 100% exists in the directory.. Ideally I want to avoid creating Solution Items folder myself and let the API handle it..
When iterating the solution items/projects I am casting the items to type Project, however, during the debugging I can see that the item is also represented by type System.__ComObject and Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.Automation.OAProject however it's not possible to cast the item to either of the 2 types.. What are these other 2 types (or the correlation between 3 types) and how do I know exactly which type I am supposed to cast the solution items to?
What is the use-case for finding the commands executed by VS? I have downloaded the command explorer and captured the Project.AddExistingItem command, but how do I actually use this knowledge to execute the command from code? Here is how the command looks like:
I would be glad to hear some explanations about the questions I mentioned above.. My experience with the VS extension building so far - it's lots of googling and trial and error.. (as opposed to knowing actually what to do :) )
I´m currently developing an iOS App with Xamarin and ran into a strange error with sqlite-net-pcl:
{SQLite.SQLiteException: near ")": syntax error at SQLite.SQLite3.Prepare2 (SQLitePCL.sqlite3 db, System.String query) [0x0001e] in <49ac49cfb94341128f6929b3ff2090ee>:0 at SQLite.PreparedSqlLiteInsertCommand.Prepare () [0x00011] in <49ac49cfb94341128f6929b…}
The error occours when I want to insert into a table of the following model:
public class PPPCount
{
public PPPCount()
{
}
[PrimaryKey]
public int Id { get; set; }
public string PerpePartCount { get; set; }
}
Here is the calling code:
try
{
var con = await DbFactory.Instance();
var perpetrationPartCount = await
service.GetSumPerpetrationParts(immobilePerpetrationId);
var dbModel = await con.FindAsync<PPPCount>(immobilePerpetrationId);
if (dbModel == null)
{
var model = new PPPCount();
model.Id = immobilePerpetrationId;
model.PerpePartCount = perpetrationPartCount;
//This causes the exception!!!!
await con.InsertAsync(perpetrationPartCount);
}
else
{
if (dbModel.PerpePartCount != perpetrationPartCount)
{
dbModel.PerpePartCount = perpetrationPartCount;
await con.UpdateAsync(dbModel);
}
}
return perpetrationPartCount;
}
catch (Exception e)
{
//AlertHelper.ShowError(e.Message);
}
The code of the DbFactory that creates and holds my sqlite connection object:
public class DbFactory
{
private static SQLiteAsyncConnection connection;
public static async Task<SQLiteAsyncConnection> Instance()
{
if (connection == null)
{
bool deleteDb = false;
string folder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var dbPath = System.IO.Path.Combine(folder, "..", "immotech_offline.db3");
if (File.Exists(dbPath) && deleteDb)
{
File.Delete(dbPath);
}
connection = new SQLiteAsyncConnection(dbPath);
try
{
await connection.CreateTableAsync<ImmobilePerpetration>();
await connection.CreateTableAsync<Customer>();
await connection.CreateTableAsync<PPPCount>();
}
catch (Exception e)
{
//TODO: Delete this part!
int i = 0;
}
}
return connection;
}
}
The strange thing is I can work with the two other models without any problems!
I really can´t explain what is causing this error and I tried to enable the tracing to show the SQL-Statements, but unfortunatelly the Async version of the sqlite connection object doesn´t provide a tracing.
Then I tried the same code in synchronous version with tracing enabled and this is the result:
insert into "Int32"() values ()
Well this is very clear why it isn´t working, but how can such a statement be generated? Did I missed something or it is a bug?
UPDATE: Yes I used the search function and no case fits for my problem.
Should this:
await con.InsertAsync(perpetrationPartCount);
be:
await con.InsertAsync(model);
We can't tell what type perpetrationPartCount is based on your code. It might not be a PPPCount and therefore that entity might not be the issue.
I am using this GetBasicPropertiesAsync method to fetch file properties one by one in a loop.
But I land up with this error:
WinRT Information : Cannot call the requested method
(GetBasicPropertiesAsync). A previous call to this method is pending
and must return before the method can be called again.
My main function has the following code to enumerate all files and folders from the pictures library.
List<BaseStorage> listOfFiles = new List<BaseStorage>();
IReadOnlyList<StorageFolder> foldersList = await curFolder.MCMFolder.GetFoldersAsync();
// For each folder found ...
foreach (StorageFolder folder in foldersList)
{
listOfFiles.Add(new Folder(folder, parents));
}
// Enumerate all files in the Pictures library.
IReadOnlyList<StorageFile> fileList = await curFolder.MCMFolder.GetFilesAsync();
// For each file found ...
foreach (StorageFile file in fileList)
{
listOfFiles.Add(new Document(file));
}
return listOfFiles;
Folder and Document class inherits BaseStorage Class.
class BaseStorage
{
public BaseStorage(IStorageItem storageItem)
{
this.Name = storageItem.Name;
this.CreationDate = storageItem.DateCreated.ToString();
setModifiedDateAndOwner(storageItem);
}
private async void setModifiedDateAndOwner(IStorageItem storageItem)
{
// await Task.Delay(500);
Windows.Foundation.IAsyncOperation<BasicProperties> basicPropsTask = storageItem.GetBasicPropertiesAsync();
BasicProperties _basicProps = await storageItem.GetBasicPropertiesAsync();
this.ModifiedDate = _basicProps.DateModified.ToString();
string fileOwnerProperty = "System.FileOwner";
List<string> propertiesToFetch = new List<string>();
propertiesToFetch.Add(fileOwnerProperty);
IDictionary<string, object> props = await _basicProps.RetrievePropertiesAsync(propertiesToFetch);
this.Owner = props[fileOwnerProperty].ToString();
return;
}
}
class Document
{
public Document()
{
setSize();
}
private async void setSize()
{
BasicProperties _basicProps = await file.GetBasicPropertiesAsync();
ulong fileSizeInBytes = _basicProps.Size;
}
}
The problem here is the method setModifiedDateAndOwner has a call to GetBasicPropertiesAsync method. Even before this method is complete, the
child class - Document calls setSize method which again has a call to GetBasicPropertiesAsync method.
This causes the exception to occur. However the behaviour is not very consistent due to threads.
How do I make sure that the method setModifiedDateAndOwner in the Base class is complete before calling the methods in its child class.
Constructors can't be async, but there are some ways to work around that.
Since you're using inheritance, you can't use the Factory Pattern from the above article directly, but you can combine the Factory Pattern for the derived class with a version of the The Asynchronous Initialization Pattern for the base class:
class BaseStorage
{
protected BaseStorage(IStorageItem storageItem)
{
this.Name = storageItem.Name;
this.CreationDate = storageItem.DateCreated.ToString();
Initialization = setModifiedDateAndOwner(storageItem);
}
protected Task Initialization { get; private set; }
private async Task setModifiedDateAndOwner(IStorageItem storageItem)
{
…
}
}
class Document : BaseStorage
{
private Document(IStorageFile storageFile)
: base(storageFile)
{ }
public static async Task<Document> Create(IStorageFile storageFile)
{
var result = new Document(storageFile);
await result.Initialization;
return result;
}
}
This way, you have to be careful when implementing Document and other classes that inherit from BaseStorage, but you can only use those classes correctly (you have to await the Task to get the created instance).
I would like to use Entity Framework Code first approach with SQLCE4 database. Everything seems to be really nice but I have problem with debugging sql queries. I found that EFTracing from http://efwrappers.codeplex.com/ should be exactly what I need but I don't know how to use it without app.config file. I am not big fan of this configuration. I want to use only C# code to set everything up and running. I think it should be fine to use code like this:
using (System.Data.Common.DbConnection c =
new EFTracingProvider.EFTracingConnection(
new System.Data.SqlServerCe.SqlCeConnection(conn)))
{
using (var context = new MyContext(c))
{
var a = from data in context.Projects select data;
}
}
But it doesn't work. It throws exception:
Unable to determine the provider name for connection of type
EFTracingProvider.EFTracingConnection'.
Is there any simple way how to correctly create wrapped connection only in code?
Solution for my problem is following DbContext object.
public class MyContext : DbContext
{
public MyContext()
: base(CreateConnection("Data Source=file.sdf",
"System.Data.SqlServerCe.4.0"), true)
{ }
public DbSet<Project> Projects { get; set; }
public static bool TraceEnabled = true;
private static DbConnection CreateConnection(string connectionString,
string providerInvariantName)
{
DbConnection connection = null;
if (TraceEnabled)
{
EFTracingProviderConfiguration.RegisterProvider();
EFTracingProviderConfiguration.LogToConsole = true;
string wrapperConnectionString = String.Format(#"wrappedProvider={0};{1}",
providerInvariantName, connectionString);
connection = new EFTracingConnection()
{
ConnectionString = wrapperConnectionString
};
}
else
{
DbProviderFactory factory = DbProviderFactories.GetFactory(providerInvariantName);
connection = factory.CreateConnection();
connection.ConnectionString = connectionString;
}
return connection;
}
}
So now I can use just context and connection is created automatically for wrapped or unwrapped SqlCe depending on TraceEnabled property.
using (var context = new MyContext())
{
var a = context.Projects.FirstOrDefault();
}
The genuine way to trace SQL queries is to call the ToString method like that :
var t = from c in _entities.CompanyDetail
select c;
string test = t.ToString();
I don't know EFTracing, but you might want to try MVCMiniProfiler. Despite the name MVCMiniProfiler also provide SQL queries profiling and work without config file.
I've done this by creating a wrapper class around the ObjectContext and using that wrapper instead of the original context. Here's an example context wrapper:
public partial class LoggedContext : MyContext
{
public LoggedContext()
: this("name=MyEntities") // Adjust this to match your entities
{
}
public LoggedContext(string connectionString)
: base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(connectionString)
{
}
private EFTracingConnection TracingConnection
{
get { return this.UnwrapConnection<EFTracingConnection>(); }
}
public event EventHandler<CommandExecutionEventArgs> CommandExecuting
{
add { this.TracingConnection.CommandExecuting += value; }
remove { this.TracingConnection.CommandExecuting -= value; }
}
public event EventHandler<CommandExecutionEventArgs> CommandFinished
{
add { this.TracingConnection.CommandFinished += value; }
remove { this.TracingConnection.CommandFinished -= value; }
}
public event EventHandler<CommandExecutionEventArgs> CommandFailed
{
add { this.TracingConnection.CommandFailed += value; }
remove { this.TracingConnection.CommandFailed -= value; }
}
}
I also have a static class that defines the tracing output method and has a static method to initialize tracing. Here:
public static class EFTracingExtensions
{
private static ILogger _logger;
public static void InitSqlTracing(ILogger logger)
{
_logger = logger;
EFTracingProviderConfiguration.RegisterProvider();
if (logger.IsLoggingEnabled()) // Don't add logging hooks if logging isn't enabled
{
EFTracingProviderConfiguration.LogAction = new Action<CommandExecutionEventArgs>(AppendSqlLog);
}
}
private static void AppendSqlLog(CommandExecutionEventArgs e)
{
if (e.Status != CommandExecutionStatus.Executing) // we only care about Finished and Failed
{
StringBuilder msg = new StringBuilder(e.ToTraceString().TrimEnd());
msg.Append(Environment.NewLine);
if (e.Result is SqlDataReader)
{
int rows = ((SqlDataReader)e.Result).HasRows ? ((SqlDataReader)e.Result).RecordsAffected : 0;
msg.AppendFormat("*** {0} rows affected", rows);
}
else if (e.Result is int)
{
msg.AppendFormat("*** result: {0}", e.Result);
}
else
{
msg.AppendFormat("*** finished, result: {0}", e.Result);
}
msg.Append(Environment.NewLine);
msg.AppendFormat(" [{0}] [{1}] in {2} seconds", e.Method, e.Status, e.Duration);
_logger.Log(msg.ToString(), LoggerCategories.SQL);
}
}
}
ILogger is the logging interface I'm using. You need to substitute your own interface/methods.
The InitSqlTracing method is invoked once when my program starts up, and then the LoggedContext class is used to log all the SQL generated by Entity Framework.
Putting it all together with your sample code:
EFTracingExtensions.InitSqlTracing(logger); // only call this once
using (var context = new LoggedContext())
{
var a = from data in context.Projects select data;
}