switch to sqlite
This commit is contained in:
@ -1,9 +1,12 @@
|
||||
using System.Xml.Linq;
|
||||
using SQLite;
|
||||
|
||||
namespace Billing.Models
|
||||
{
|
||||
public class Account : BaseModel
|
||||
public class Account : IIdItem
|
||||
{
|
||||
private const string ICON_DEFAULT = "ic_default";
|
||||
|
||||
[PrimaryKey, AutoIncrement]
|
||||
public int Id { get; set; }
|
||||
public string Icon { get; set; } = ICON_DEFAULT;
|
||||
public AccountCategory Category { get; set; }
|
||||
@ -11,28 +14,6 @@ namespace Billing.Models
|
||||
public decimal Initial { get; set; }
|
||||
public decimal Balance { get; set; }
|
||||
public string Memo { get; set; }
|
||||
|
||||
public override void OnXmlDeserialize(XElement node)
|
||||
{
|
||||
Id = Read(node, nameof(Id), 0);
|
||||
Icon = Read(node, nameof(Icon), ICON_DEFAULT);
|
||||
Category = (AccountCategory)Read(node, nameof(Category), 0);
|
||||
Name = Read(node, nameof(Name), string.Empty);
|
||||
Initial = Read(node, nameof(Initial), 0m);
|
||||
Balance = Read(node, nameof(Balance), 0m);
|
||||
Memo = Read(node, nameof(Memo), null);
|
||||
}
|
||||
|
||||
public override void OnXmlSerialize(XElement node)
|
||||
{
|
||||
Write(node, nameof(Id), Id);
|
||||
Write(node, nameof(Icon), Icon);
|
||||
Write(node, nameof(Category), (int)Category);
|
||||
Write(node, nameof(Name), Name);
|
||||
Write(node, nameof(Initial), Initial);
|
||||
Write(node, nameof(Balance), Balance);
|
||||
Write(node, nameof(Memo), Memo);
|
||||
}
|
||||
}
|
||||
|
||||
public enum AccountCategory
|
||||
|
@ -1,186 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Billing.Models
|
||||
{
|
||||
public interface IModel
|
||||
{
|
||||
void OnXmlSerialize(XElement node);
|
||||
|
||||
void OnXmlDeserialize(XElement node);
|
||||
}
|
||||
|
||||
public abstract class BaseModel : IModel, IDisposable
|
||||
{
|
||||
public const string ICON_DEFAULT = "ic_default";
|
||||
|
||||
private bool disposed = false;
|
||||
|
||||
public static T ParseXml<T>(string xml) where T : BaseModel, new()
|
||||
{
|
||||
XDocument doc = XDocument.Parse(xml);
|
||||
T model = new();
|
||||
model.OnXmlDeserialize(doc.Root);
|
||||
return model;
|
||||
}
|
||||
|
||||
protected static string ToString(IFormattable v) => v.ToString(null, CultureInfo.InvariantCulture);
|
||||
protected static double ParseDouble(string s) => double.Parse(s, CultureInfo.InvariantCulture);
|
||||
protected static decimal ParseDecimal(string s) => decimal.Parse(s, CultureInfo.InvariantCulture);
|
||||
protected static int ParseInt(string s) => int.Parse(s, CultureInfo.InvariantCulture);
|
||||
protected static long ParseLong(string s) => long.Parse(s, CultureInfo.InvariantCulture);
|
||||
|
||||
protected static bool IsTrue(string s) =>
|
||||
string.Equals(s, "true", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(s, "yes", StringComparison.OrdinalIgnoreCase) ||
|
||||
s == "1";
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static XElement WriteString(XElement parent, string name, string val, Action<XElement> action = null)
|
||||
{
|
||||
XElement ele;
|
||||
if (val == null)
|
||||
{
|
||||
ele = new XElement(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
ele = new XElement(name, val);
|
||||
}
|
||||
action?.Invoke(ele);
|
||||
parent.Add(ele);
|
||||
return ele;
|
||||
}
|
||||
|
||||
private static T ReadSubnode<T>(XElement node, string subname, Func<XElement, T> func)
|
||||
{
|
||||
var ele = node.Elements().FirstOrDefault(e => string.Equals(e.Name.ToString(), subname, StringComparison.OrdinalIgnoreCase));
|
||||
return func(ele);
|
||||
}
|
||||
|
||||
private static T ReadObject<T>(XElement node) where T : IModel, new()
|
||||
{
|
||||
if (IsTrue(node.Attribute("null")?.Value))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
T value = new();
|
||||
value.OnXmlDeserialize(node);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static T[] ReadArray<T>(XElement node) where T : IModel, new()
|
||||
{
|
||||
if (IsTrue(node.Attribute("null")?.Value))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
int count = ParseInt(node.Attribute("count").Value);
|
||||
T[] array = new T[count];
|
||||
foreach (XElement ele in node.Elements("item"))
|
||||
{
|
||||
int index = ParseInt(ele.Attribute("index").Value);
|
||||
array[index] = ReadObject<T>(ele);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Read/Write Helper
|
||||
|
||||
protected static XElement Write(XElement parent, string name, string val) => WriteString(parent, name, val);
|
||||
protected static XElement Write(XElement parent, string name, DateTime date) => WriteString(parent, name, ToString(date.Ticks));
|
||||
protected static XElement Write<T>(XElement parent, string name, T value) where T : IFormattable => WriteString(parent, name, ToString(value));
|
||||
protected static XElement WriteObject<T>(XElement parent, string name, T value) where T : IModel => WriteString(parent, name, null,
|
||||
action: ele =>
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
ele.Add(new XAttribute("null", 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
value.OnXmlSerialize(ele);
|
||||
}
|
||||
});
|
||||
protected static XElement WriteArray<T>(XElement parent, string name, T[] value) where T : IModel => WriteString(parent, name, null,
|
||||
action: ele =>
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
ele.Add(new XAttribute("null", 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
ele.Add(new XAttribute("count", value.Length));
|
||||
for (var i = 0; i < value.Length; i++)
|
||||
{
|
||||
XElement item = WriteObject(ele, "item", value[i]);
|
||||
item.Add(new XAttribute("index", i));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
protected static string Read(XElement node, string subname, string def) => ReadSubnode(node, subname, e => e?.Value ?? def);
|
||||
protected static int Read(XElement node, string subname, int def) => ReadSubnode(node, subname, e => e == null ? def : ParseInt(e.Value));
|
||||
protected static long Read(XElement node, string subname, long def) => ReadSubnode(node, subname, e => e == null ? def : ParseLong(e.Value));
|
||||
protected static double Read(XElement node, string subname, double def) => ReadSubnode(node, subname, e => e == null ? def : ParseDouble(e.Value));
|
||||
protected static decimal Read(XElement node, string subname, decimal def) => ReadSubnode(node, subname, e => e == null ? def : ParseDecimal(e.Value));
|
||||
protected static DateTime Read(XElement node, string subname, DateTime def) => ReadSubnode(node, subname, e => e == null ? def : new DateTime(ParseLong(e.Value)));
|
||||
protected static T ReadObject<T>(XElement node, string subname, T def = default) where T : IModel, new() => ReadSubnode(node, subname, e => e == null ? def : ReadObject<T>(e));
|
||||
protected static T[] ReadArray<T>(XElement node, string subname, T[] def = null) where T : IModel, new() => ReadSubnode(node, subname, e => e == null ? def : ReadArray<T>(e));
|
||||
|
||||
#endregion
|
||||
|
||||
public XDocument ToXml()
|
||||
{
|
||||
XDocument xdoc = new(new XDeclaration("1.0", "utf-8", "yes"), new XElement("root"));
|
||||
ToXml(xdoc.Root);
|
||||
return xdoc;
|
||||
}
|
||||
|
||||
public void ToXml(XElement node)
|
||||
{
|
||||
OnXmlSerialize(node);
|
||||
}
|
||||
|
||||
public void SaveToStream(Stream stream)
|
||||
{
|
||||
ToXml().Save(stream, SaveOptions.DisableFormatting);
|
||||
}
|
||||
|
||||
public abstract void OnXmlSerialize(XElement node);
|
||||
public abstract void OnXmlDeserialize(XElement node);
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
using MemoryStream ms = new();
|
||||
//using StreamWriter writer = new(ms, Encoding.UTF8);
|
||||
//XDocument xdoc = ToXml();
|
||||
//xdoc.Save(writer, SaveOptions.DisableFormatting);
|
||||
//writer.Flush();
|
||||
SaveToStream(ms);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
using StreamReader reader = new(ms, Encoding.UTF8);
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool dispose) { }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
Dispose(true);
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using SQLite;
|
||||
|
||||
namespace Billing.Models
|
||||
{
|
||||
public class Bill : BaseModel
|
||||
public class Bill : IIdItem
|
||||
{
|
||||
[PrimaryKey, AutoIncrement]
|
||||
public int Id { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public string Name { get; set; }
|
||||
@ -13,29 +14,5 @@ namespace Billing.Models
|
||||
public string Store { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
public string Note { get; set; }
|
||||
|
||||
public override void OnXmlDeserialize(XElement node)
|
||||
{
|
||||
Id = Read(node, nameof(Id), 0);
|
||||
Amount = Read(node, nameof(Amount), 0m);
|
||||
Name = Read(node, nameof(Name), string.Empty);
|
||||
CategoryId = Read(node, nameof(CategoryId), -1);
|
||||
WalletId = Read(node, nameof(WalletId), -1);
|
||||
Store = Read(node, nameof(Store), string.Empty);
|
||||
CreateTime = Read(node, nameof(CreateTime), default(DateTime));
|
||||
Note = Read(node, nameof(Note), string.Empty);
|
||||
}
|
||||
|
||||
public override void OnXmlSerialize(XElement node)
|
||||
{
|
||||
Write(node, nameof(Id), Id);
|
||||
Write(node, nameof(Amount), Amount);
|
||||
Write(node, nameof(Name), Name);
|
||||
Write(node, nameof(CategoryId), CategoryId);
|
||||
Write(node, nameof(WalletId), WalletId);
|
||||
Write(node, nameof(Store), Store);
|
||||
Write(node, nameof(CreateTime), CreateTime);
|
||||
Write(node, nameof(Note), Note);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +1,19 @@
|
||||
using Xamarin.Forms;
|
||||
using System.Xml.Linq;
|
||||
using SQLite;
|
||||
|
||||
namespace Billing.Models
|
||||
{
|
||||
public class Category : BaseModel
|
||||
public class Category : IIdItem
|
||||
{
|
||||
private const string ICON_DEFAULT = "ic_default";
|
||||
private const long TRANSPARENT_COLOR = 0x00ffffffL;
|
||||
|
||||
[PrimaryKey, AutoIncrement]
|
||||
public int Id { get; set; }
|
||||
public CategoryType Type { get; set; }
|
||||
public string Icon { get; set; } = ICON_DEFAULT;
|
||||
public string Name { get; set; }
|
||||
public Color TintColor { get; set; } = Color.Transparent;
|
||||
public long TintColor { get; set; } = TRANSPARENT_COLOR;
|
||||
public int? ParentId { get; set; }
|
||||
|
||||
public override void OnXmlDeserialize(XElement node)
|
||||
{
|
||||
Id = Read(node, nameof(Id), 0);
|
||||
Type = (CategoryType)Read(node, nameof(Type), 0);
|
||||
Icon = Read(node, nameof(Icon), ICON_DEFAULT);
|
||||
Name = Read(node, nameof(Name), string.Empty);
|
||||
var color = Read(node, nameof(TintColor), string.Empty);
|
||||
if (!string.IsNullOrEmpty(color))
|
||||
{
|
||||
TintColor = Color.FromHex(color);
|
||||
}
|
||||
var parentId = Read(node, nameof(ParentId), -1);
|
||||
if (parentId >= 0)
|
||||
{
|
||||
ParentId = parentId;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnXmlSerialize(XElement node)
|
||||
{
|
||||
Write(node, nameof(Id), Id);
|
||||
Write(node, nameof(Type), (int)Type);
|
||||
Write(node, nameof(Icon), Icon);
|
||||
Write(node, nameof(Name), Name);
|
||||
if (TintColor != Color.Transparent)
|
||||
{
|
||||
Write(node, nameof(TintColor), TintColor.ToHex());
|
||||
}
|
||||
if (ParentId != null)
|
||||
{
|
||||
Write(node, nameof(ParentId), ParentId.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum CategoryType
|
||||
|
7
Billing.Shared/Models/IIdItem.cs
Normal file
7
Billing.Shared/Models/IIdItem.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Billing.Models
|
||||
{
|
||||
public interface IIdItem
|
||||
{
|
||||
public int Id { get; }
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user