做个windows 8开发的或者正要做windows 8开发的需要知道一点是,在win8目前的SDK中是不支持本地数据库的。据说现在有win8版的sqllite数据库了,我没用过,不过就算真的有也没关系,我这篇博客不是讲如果用使用数据库的,而是讲如果利用现有的API和资源做一个自定义的数据库。如果你觉得使用SQllite够用的话那你可以跳过这篇文章。
首先win8是没有本地数据库的(当前win8SDK版本),其次我们的APP可能就是需要数据库来存放一些客户端的东西,那么目前的唯一的方法就是自己做数据库,其实这里说自己做数据库说的有点大,因为我这里给大家介绍的不是说自己开发一个类似sqllite那种真正的数据库(我没这本事),而是给大家介绍一种方法可以达到类似WP7 中SQL CE那样使用习惯和使用方法的伪数据库(也可以称为山寨版数据库),如果你用过WP7的SQL CE的数据库,你会对我介绍的方法很熟悉,用起来会很顺手。
废话不多说,进入正题。
首先数据库的存储方式肯定不是存储在内存里的,而是以文件的形式存储在“硬盘”上的。另外,既然是数据库,那么数据在内存中的状态应该是以集合形式存储的。这样我们的问题就是,把集合以文件的形式存储,如何达到这个目的,很显然就是利用序列化和反序列化。目前数据的序列化有两种方式,一个是XML,一个JSON,我个人推荐Json,速度快,序列化后的体积也小。
下面正是进入编码阶段,新建一个项目叫做DatabaseTest,然后新建一个类库项目Database,记得引用下。先对DataBase进行修改,既然我们的数据库不是真数据库,那么我就取名叫FakeDatabase,这是一个基类,所有以后用到数据库的地方必须继承自这个类。
先不管FakeDatabase,继续新建一个类叫Table,继承自ObservableCollection,这个类是一个密封类,看名字就知道数数据库中的表,代码如下:
public sealed class Table<T> : ObservableCollection<T> { internal Table() { } protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { base.OnCollectionChanged(e); IsChanged = true; if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add || e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace) { foreach (INotifyPropertyChanged item in e.NewItems) { item.PropertyChanged += item_PropertyChanged; } } } //指示表中的数据是否发生更改 internal bool IsChanged; void item_PropertyChanged(object sender, PropertyChangedEventArgs e) { IsChanged = true; } }
然后回到FakeDataBase。首先我们的目标是做一个在使用方法和使用习惯上类似WP7 SQLCe的数据库,那么就是说在新建数据库的时候,只要在数据库类中添加相应的Table就行了。我不说具体实现过程了,直接贴代码好了:
/// <summary> /// 伪数据库基类 /// </summary> public abstract class FakeDataBase { private string DBAddress; public FakeDataBase(string dbAddress) { this.DBAddress = dbAddress; } private bool isInitialed; /// <summary> /// 初始化数据库中的数据 /// </summary> /// <returns></returns> public virtual async Task InitialBatabaseAsync() { if (!isInitialed) { Type t = this.GetType(); foreach (var item in t.GetTypeInfo().DeclaredFields) { if (item.FieldType.Name == "Table`1") { string address = DBAddress + "_" + item.Name; var value = await DeserializeObjectFromFile(address, item.FieldType); if (value == null) { item.SetValue(this, CreateInstance(item.FieldType, null)); } else item.SetValue(this, value); } } isInitialed = true; } } /// <summary> /// 提交更改 /// </summary> /// <returns></returns> public virtual async Task SubmitChanges() { Type t = this.GetType(); foreach (var item in t.GetTypeInfo().DeclaredFields) { var itemType = item.FieldType; if (itemType.Name == "Table`1") { var info = itemType.GetTypeInfo().DeclaredFields.FirstOrDefault(c => c.Name == "IsChanged"); var table = item.GetValue(this); var value = (bool)info.GetValue(table); if (value) { string address = DBAddress + "_" + item.Name; await SerializeObjectToFile(address, item.GetValue(this)); info.SetValue(table, false); } } } } /// <summary> /// 根据文件名,将文件内容反序列化成某个对象 /// </summary> /// <param name="fileName"></param> /// <param name="type"></param> /// <returns></returns> private async Task<object> DeserializeObjectFromFile(string fileName, Type type) { StorageFolder storageFolder = ApplicationData.Current.LocalFolder; StorageFile file = null; object objTarget; try { file = await storageFolder.GetFileAsync(fileName); } catch (Exception) { } if (file == null) { objTarget = null; } else { string str = await FileIO.ReadTextAsync(file); objTarget = JsonDeserialize(str, type); } return objTarget; } /// <summary> /// 将Json字符串反序列化成对象 /// </summary> /// <param name="str"></param> /// <param name="type"></param> /// <returns></returns> private object JsonDeserialize(string str, Type type) { if (string.IsNullOrEmpty(str)) return null; DataContractJsonSerializer serializer = new DataContractJsonSerializer(type); using (Stream stream = new MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(str))) { return serializer.ReadObject(stream); } } /// <summary> /// 将某个对象序列化成Json字符串 /// </summary> /// <param name="target"></param> /// <returns></returns> private static string JsonSerializer(object target) { DataContractJsonSerializer serializer = new DataContractJsonSerializer(target.GetType()); using (Stream stream = new MemoryStream()) { serializer.WriteObject(stream, target); stream.Seek(0, SeekOrigin.Begin); using (StreamReader reader = new StreamReader(stream)) { return reader.ReadToEnd(); } } } /// <summary> /// 将某个对象序列化成文件,如果传递的对象为null,那么删除原来的文件 /// </summary> /// <param name="fileName"></param> /// <param name="target"></param> /// <returns></returns> private async Task SerializeObjectToFile(string fileName, object target) { StorageFolder storageFolder = ApplicationData.Current.LocalFolder; StorageFile storageFile = null; //如果target为null,那么删除文件 if (target == null) { storageFile = await storageFolder.GetFileAsync(fileName); if (storageFile != null) { await storageFile.DeleteAsync(); } return; } string str = JsonSerializer(target); storageFile = await storageFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting); await FileIO.WriteTextAsync(storageFile, str); } /// <summary> /// 动态创建一个类型的对象 /// </summary> /// <param name="t"></param> /// <param name="paramas"></param> /// <returns></returns> private object CreateInstance(Type t, object[] paramas) { int pCount = paramas == null ? 0 : paramas.Length; foreach (var item in t.GetTypeInfo().DeclaredConstructors) { var p = item.GetParameters(); if (p.Length == pCount) return item.Invoke(paramas); } throw new InvalidOperationException("没有找到合适的构造函数"); } }
上面两部分的代码已经实现了我们上面提到的目标。下面我就介绍下如何使用这个自己做的伪数据库。
假如我们数据库中有一个用来存放用户信息的表,叫UserInfo,里面有name和age两个属性,那么新建一个UserInfo的类。
[DataContract] public class UserInfo : BindableBase { private string name; [DataMember] public string Name { get { return name; } set { base.SetProperty(ref name, value, "Name"); } } private int age; [DataMember] public int Age { get { return age; } set { base.SetProperty(ref age, value, "Age"); } } }
然后就是创建我们的数据库了,叫做TestDatabase
public class TestDataBase:FakeDataBase { private const string DBAddress = "TestDataBase.db"; public TestDataBase() : base(DBAddress) { } public Table<UserInfo> UserTable; }
至此,数据库已经创建完毕。
下面讲下如何操作这个数据库。
首先我们在使用数据库中数据库的时候一定要先初始化数据库,
Data.TestDataBase db; db = new Data.TestDataBase(); await db.InitialBatabaseAsync();
然后就是把数据库中的表绑定到列表控件
listview_userlist.ItemsSource = db.UserTable;
往数据库中添加数据:
db.UserTable.Add(addUser);
await db.SubmitChanges();
删除数据代码:
db.UserTable.Remove(user);
await db.SubmitChanges();
以上操作数据库的代码是不是看起来很熟悉?只要你用过WP7 的SQL CE,看到这些代码你肯定会很熟悉的。
界面代码我就不贴出来了,你可以直接下载源码查看的,下面贴一张运行图。
现在说下这个伪数据库和WP7 SQLce比起来的优缺点:
先说优点:
1.数据库中表格可以直接作为数据源帮顶到列表控件(WP7 SQL CE虽然能作为数据源,但是数据源如果有增删的操作,那么列表控件是反应不出来的)
2.如果表的结构发生改变你不用担心兼容性的问题(有一点需要注意,就是类型的改变需要考虑下的,比如原来是简单类型的,后来改成复杂类型了,那么你还是另外加一个属性吧)
3.表的结构是可以使用复杂类型作为属性的,只要那个复杂类型标记了[DataContract]和[DataMenber]就行。
缺点:
1.虽然也支持一个数据库中存在多个表,但是表之间不支持关联。
2.数据表没有索引和主键,你非要有主键的话可以在往表中添加数据的时候自己加上。
3.数据量比较大的时候会有性能问题(几万条数据那种)
4.数据的存储没有加密,不过这个问题其实可以解决的。
以上提到的缺点,说实话对于一般的数据存储来说其实没什么问题的。除非那些对性能(在数据量小的情况下几乎不会有这个问题)和安全要求较高的就不适合使用这种方法。
以上所提到的方法是我在开发自己的APP中所用的方法,虽然不是完美的解决方案,但是作为应急用搓搓有余了。
说到底是一个山寨版的数据库,欢迎拍砖!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!