switch to sqlite
This commit is contained in:
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Billing.Models;
|
||||
using Billing.UI;
|
||||
using SQLite;
|
||||
using Xamarin.Essentials;
|
||||
using Resource = Billing.Languages.Resource;
|
||||
|
||||
@ -14,50 +15,20 @@ namespace Billing.Store
|
||||
public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
|
||||
public static readonly string CacheFolder = FileSystem.CacheDirectory;
|
||||
|
||||
private const string accountFile = "accounts.xml";
|
||||
private const string billFile = "bills.xml";
|
||||
private const string categoryFile = "categories.xml";
|
||||
#region Sqlite3
|
||||
private const string dbfile = "data.db3";
|
||||
private static SQLiteAsyncConnection database;
|
||||
#endregion
|
||||
|
||||
private static StoreHelper instance;
|
||||
private static StoreHelper Instance => instance ??= new StoreHelper();
|
||||
|
||||
public static List<Account> GetAccounts() => Instance.GetAccountsInternal();
|
||||
public static void WriteAccounts(IEnumerable<Account> accounts) => Instance.WriteAccountsInternal(accounts);
|
||||
public static List<Bill> GetBills() => Instance.GetBillsInternal();
|
||||
public static void WriteBills(IEnumerable<Bill> bills) => Instance.WriteBillsInternal(bills);
|
||||
public static List<Category> GetCategories() => Instance.GetCategoriesInternal();
|
||||
public static void WriteCategories(IEnumerable<Category> categories) => Instance.WriteCategoriesInternal(categories);
|
||||
|
||||
private StoreHelper() { }
|
||||
|
||||
private List<Account> GetAccountsInternal()
|
||||
private static readonly AsyncLazy<StoreHelper> Instance = new(async () =>
|
||||
{
|
||||
return GetList<Account>(Path.Combine(PersonalFolder, accountFile));
|
||||
}
|
||||
|
||||
private void WriteAccountsInternal(IEnumerable<Account> accounts)
|
||||
{
|
||||
var filename = Path.Combine(PersonalFolder, accountFile);
|
||||
WriteList(filename, accounts);
|
||||
}
|
||||
|
||||
private List<Bill> GetBillsInternal()
|
||||
{
|
||||
return GetList<Bill>(Path.Combine(PersonalFolder, billFile));
|
||||
}
|
||||
|
||||
private void WriteBillsInternal(IEnumerable<Bill> bills)
|
||||
{
|
||||
var filename = Path.Combine(PersonalFolder, billFile);
|
||||
WriteList(filename, bills);
|
||||
}
|
||||
|
||||
private List<Category> GetCategoriesInternal()
|
||||
{
|
||||
var list = GetList<Category>(Path.Combine(PersonalFolder, categoryFile));
|
||||
if (list == null || list.Count == 0)
|
||||
var instance = new StoreHelper();
|
||||
await database.CreateTablesAsync<Category, Account, Bill>();
|
||||
var count = await database.ExecuteScalarAsync<int>("SELECT COUNT(Id) FROM [Category]");
|
||||
if (count <= 0)
|
||||
{
|
||||
list = new List<Category>
|
||||
// init categories
|
||||
await database.InsertAllAsync(new List<Category>
|
||||
{
|
||||
// sample categories
|
||||
new() { Id = 1, Name = Resource.Clothing, Icon = "clothes" },
|
||||
@ -89,53 +60,145 @@ namespace Billing.Store
|
||||
new() { Id = 113, ParentId = 6, Name = Resource.Party, Icon = "party" },
|
||||
new() { Id = 200, ParentId = 10, Type = CategoryType.Income, Name = Resource.Salary, Icon = "#brand#buffer" },
|
||||
new() { Id = 201, ParentId = 10, Type = CategoryType.Income, Name = Resource.Bonus, Icon = "dollar" },
|
||||
};
|
||||
Task.Run(() => WriteCategoriesInternal(list));
|
||||
});
|
||||
}
|
||||
return list;
|
||||
return instance;
|
||||
});
|
||||
|
||||
public static async Task<List<Account>> GetAccountsAsync()
|
||||
{
|
||||
var instance = await Instance;
|
||||
return await instance.GetListAsync<Account>();
|
||||
}
|
||||
public static async Task<int> SaveAccountItemAsync(Account account)
|
||||
{
|
||||
var instance = await Instance;
|
||||
return await instance.SaveItemAsync(account);
|
||||
}
|
||||
public static async Task<int> DeleteAccountItemAsync(Account account)
|
||||
{
|
||||
var instance = await Instance;
|
||||
return await instance.DeleteItemAsync(account);
|
||||
}
|
||||
|
||||
private void WriteCategoriesInternal(IEnumerable<Category> categories)
|
||||
public static async Task<List<Bill>> GetBillsAsync()
|
||||
{
|
||||
var filename = Path.Combine(PersonalFolder, categoryFile);
|
||||
WriteList(filename, categories);
|
||||
var instance = await Instance;
|
||||
return await instance.GetListAsync<Bill>();
|
||||
}
|
||||
public static async Task<int> SaveBillItemAsync(Bill bill)
|
||||
{
|
||||
var instance = await Instance;
|
||||
return await instance.SaveItemAsync(bill);
|
||||
}
|
||||
public static async Task<int> DeleteBillItemAsync(Bill bill)
|
||||
{
|
||||
var instance = await Instance;
|
||||
return await instance.DeleteItemAsync(bill);
|
||||
}
|
||||
|
||||
public static async Task<List<Category>> GetCategoriesAsync()
|
||||
{
|
||||
var instance = await Instance;
|
||||
return await instance.GetListAsync<Category>();
|
||||
}
|
||||
public static async Task<int> SaveCategoryItemAsync(Category category)
|
||||
{
|
||||
var instance = await Instance;
|
||||
return await instance.SaveItemAsync(category);
|
||||
}
|
||||
public static async Task<int> DeleteCategoryItemAsync(Category category)
|
||||
{
|
||||
var instance = await Instance;
|
||||
return await instance.DeleteItemAsync(category);
|
||||
}
|
||||
|
||||
private StoreHelper()
|
||||
{
|
||||
database = new SQLiteAsyncConnection(Path.Combine(PersonalFolder, dbfile),
|
||||
SQLiteOpenFlags.ReadWrite |
|
||||
SQLiteOpenFlags.Create |
|
||||
SQLiteOpenFlags.SharedCache);
|
||||
}
|
||||
|
||||
public Task<List<T>> GetListAsync<T>(string query, params object[] args) where T : new()
|
||||
{
|
||||
try
|
||||
{
|
||||
return database.QueryAsync<T>(query, args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Helper.Error("db.read", $"failed to read db, query: {string.Format(query, args)}, error: {ex.Message}");
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task<T> GetItemAsync<T>(int id) where T : IIdItem, new()
|
||||
{
|
||||
try
|
||||
{
|
||||
var source = new TaskCompletionSource<T>();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var list = await database.QueryAsync<T>($"SELECT * FROM [{typeof(T).Name}] WHERE [Id] = ?", id);
|
||||
source.SetResult(list.FirstOrDefault());
|
||||
});
|
||||
return source.Task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Helper.Error("db.read", $"failed to get item, table: {typeof(T)}, id: {id}, error: {ex.Message}");
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
#region Helper
|
||||
|
||||
private void WriteList<T>(string filename, IEnumerable<T> list) where T : IModel, new()
|
||||
private Task<List<T>> GetListAsync<T>() where T : new()
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
using var stream = File.Open(filename, FileMode.Create);
|
||||
list.ToStream(stream);
|
||||
return database.Table<T>().ToListAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Helper.Error("file.write", $"failed to write file: {filename}, error: {ex.Message}");
|
||||
Helper.Error("db.read", $"failed to read db, error: {ex.Message}");
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
private List<T> GetList<T>(string file) where T : IModel, new()
|
||||
private Task<int> SaveItemAsync<T>(T item) where T : IIdItem
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(file))
|
||||
if (item.Id < 0)
|
||||
{
|
||||
using var stream = File.OpenRead(file);
|
||||
var list = ModelExtensionHelper.FromStream<T>(stream);
|
||||
return list;
|
||||
return database.InsertAsync(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
return database.UpdateAsync(item);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Helper.Error("file.read", $"failed to read file: {file}, error: {ex.Message}");
|
||||
Helper.Error("db.write", $"failed to insert/update item, table: {typeof(T)}, id: {item.Id}, item: {item}, error: {ex.Message}");
|
||||
}
|
||||
return default;
|
||||
return Task.FromResult(-1);
|
||||
}
|
||||
|
||||
private Task<int> DeleteItemAsync<T>(T item) where T : IIdItem
|
||||
{
|
||||
try
|
||||
{
|
||||
return database.DeleteAsync(item);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Helper.Error("db.delete", $"failed to delete item, table: {typeof(T)}, id: {item.Id}, item: {item}, error: {ex.Message}");
|
||||
}
|
||||
return Task.FromResult(-1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
Reference in New Issue
Block a user