Realm数据库是SQLite、Core Data的替代品,跨平台、支持加密、更快的执行效率,不用写sql。
安装Realm
使用VS新建一个UWP项目,右键解决方案资源管理器
中项目节点下的的引用
节点,选择管理Nuget程序包
,打开NuGet
包管理器。切换到浏览
标签,搜索Realm
,选择第一项Realm
,点击右侧的安装
。
uwp_realm_01.png
创建 FodyWeavers.xml 文件
在项目中,新建一个xml文件,命名为FodyWeavers.xml
,文件属性保持默认,内容如下:
<?xml version="1.0" encoding="utf-8" ?><Weavers> <RealmWeaver /></Weavers>
The Realm package contains everything you need to use Realm. It depends on Fodyweaver, responsible for turning your RealmObject subclasses into persisted ones.
创建至少一个RealmObject派生类
//User.csclass User: RealmObject { [Realms.PrimaryKey] public int Id { get; set; } public string Name { get; set; } public string Sex { get; set; } public int Age { get; set; } }
//Book.csclass Book : RealmObject { [Realms.PrimaryKey] public int Id { get; set; } public string Name { get; set; } }
Realm数据库基本操作
连接或创建数据库
//对Realm数据库进行配置,Test.relam为数据库文件名var config = new RealmConfiguration("Test.realm");//打印一下这个数据库文件实际的保存路径System.Diagnostics.Debug.WriteLine($"{Windows.Storage.ApplicationData.Current.LocalFolder.Path}\\Test.relam");//设置数据库中的表对应的实体类,这里设置数据库只有一个User表;如果不设置,默认所有派生自RealmObject的类都会在该数据库中生成对应表 config.ObjectClasses = new Type[] { typeof(User) };//设置数据库加密密码,长度为固定的64 bytes//config.EncryptionKey = new byte[64] //{ // 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, // 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, // 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, // 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, // 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, // 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78 //};//获取Relam实例var realm = Realm.GetInstance(config);
插入记录
//插入记录var mandUser = new User() { Id = 3, Name = "Mandarava", Sex = "M", Age = 66 }; realm.Write(() =>{ realm.Add<User>(new User() { Id = 1, Name = "Tom", Sex = "M", Age = 77 }); realm.Add<User>(new User() { Id = 2, Name = "Jerry", Sex = "F", Age = 88 }); realm.Add<User>(mandUser); realm.Add<User>(new User() { Id = 4, Name = "Jack", Sex = "M", Age = 99 }); });
更新记录
//更新记录realm.Write(() => { //更新mandUser的Sex字段 mandUser.Sex = "男"; //更新mandUser //因为这里的new User的Id=3,mandUser的Id也是3,Id是PrimaryKey,所以这里仍然更新mandUser realm.Add<User>(new User() { Id = 3, Name = "鳗驼螺", Sex = "Male", Age = 88 }, true); });
查询记录
//查询记录var users = realm.All<User>().Where(user => user.Sex == "M"); foreach(var user in users) { System.Diagnostics.Debug.WriteLine($"{user.Id}\t{user.Name}\t{user.Sex}\t{user.Age}"); }
删除记录
//删除记录realm.Write(() =>{ //删除指定的记录 realm.Remove(mandUser); //删除满足条件的一组记录 var dusers = realm.All<User>().Where(user => user.Sex == "M"); realm.RemoveRange<User>(dusers); });
注:Realm.Write
方法都是基于事务处理的。
一些问题和解决方法
No RealmObjects 错误
如果在运行时出现下面的错误:
System.InvalidOperationException:“No RealmObjects. Has linker stripped them? See https://realm.io/docs/xamarin/latest/#linker-stripped-schema”
如果没有在项目中写任何RelamObject
派生类,就会出现这个问题。实际上是config.ObjectClasses
没有设置造成的。所以,至少需要创建一个RelamObject
的派生类。
非自动属性问题
Realm只支持自动属性,如:
[Realms.PrimaryKey]public int Id{get;set;}
如果写成下面这种常规属性的形式:
[Realms.PrimaryKey]public int Id { get { return id; } set { id = value; } }private int id;
你会得到类似下面的错误:
Fody/RealmWeaver: User.Id has [PrimaryKey] applied, but it's not persisted, so those attributes will be ignored.
当然,你可能会说把
[Realms.PrimaryKey]
去掉就行了。去掉是不会报错了,但这个属性仍然会被忽略,不会成为Realm数据库表的字段,因为根本问题不在于这个属性修饰符,而是它被视为it's not persisted
。
那如果我们在设置属性的时候,确有要求需要做点额外操作(比如检查属性值是否符合要求),这种情况下只能写成非自动属性,比如:
public int Age { get { return age; } set { age = value >= 0 ? value : 0; } }private int age;
但非自动属性又不能被Realm持久化,那这种情况下该怎么办?
为解决这类问题,RealmObject
提供了一个PropertyChanged
事件,该事件在属性发生改变时被触发。所以,我们要检查某个属性值是否符合我们的要求,或者在属性设置时干点其它的事,属性仍然写成自动属性,然后可以去监听这个事件,在该事件中去检查属性合法性(或干点别的),如:
//User.csclass User: RealmObject{ ... public int Age{ get;set; } }//Appvar mandUser = new User() { Id = 3, Name = "Mandarava", Sex = "M" }; mandUser.PropertyChanged += (obj, evt) => { var usr = obj as User; switch (evt.PropertyName) { case nameof(User.Age): if (usr.Age < 0) usr.Age = 0; break; default: break; } }; mandUser.Age = 66;
当然,你可能有很多个User
实例,每个都要添加一个PropertyChanged
事件,太麻烦;而且像上面的代码,你需要在监听PropertyChanged
事件后,再去设置要监听的属性Age
的值,否则就会错过监听。为避免这些麻烦,因为User
类派生自RealmObject
类,所以更好的做法是直接在User
类中重写OnPropertyChanged
事件,像这样:
//User.csprotected override void OnPropertyChanged(string propertyName){ base.OnPropertyChanged(propertyName); switch (propertyName) { case nameof(Age): if (Age < 0) Age = 0; break; default: break; } }
当然,还有一种方法是建立二个属性来解决这个问题,其中一个必是自动属性(可以设为private
):
private int age { get; set; }public int Age { get { return age; } set { age = value >= 0 ? value : 0; } }
这样也可以达到目的。当然,这个方法的问题是最终数据库中年龄字段的名称变成了age
,如果你说我一定要让字段名是Age
,咋整?可以使用MapTo
属性修饰符来限定持久化后的属性名称,像这样:
[Realms.MapTo("Age")]private int age { get; set; }
Cannot modify managed objects outside of a write transaction 错误
从Realm数据库中取出的数据对象,或通过realm.Add
已经加入到Realm数据库中的对象,在更新该对象的Realm字段值时,需要使用Realm.Write(Action)
方法(或它的异步版方法)来更新。比如,在前文中,mandUser
是通过new
来创建的,然后它被加入到Realm数据库中,这之后,它就与这个数据库有关联了,要更新它的Realm字段,不能直接像这样:
mandUser.Sex = "男";
这种方式就会导致本错误的出现,必须使用下面的方式来更新:
realm.Write(() => { mandUser.Sex = "男"; };
Write
方法是基于事务的更新,这也是为什么错误信息显示为outside of a write transaction
的原因。
那么,普通new出来的对象,与从Realm数据库中加载的数据对象有什么区别呢? 这具体看这个对象的Realm
属性值。所有派生自RealmObject
的对象,都有Realm
属性,如果这个属性值为null
,表示这个对象并没有关联Realm数据库,一个对象刚new
时,它的Realm
属性值为null
(你也可以直接用RealmObject.IsManaged
来检测,检测结果为true
就表示Realm
不为null
,这也就是出错信息中指出的所谓的managed objects
)。这种情况下,如果使用realm.Write(Action)
是没效果的(当然,它会改变这个对象的属性值,但与Realm数据库无交互),因为这条记录在realm
关联的数据库中并不存在,需要先使用realm.Add
方法来将它添加到数据库中,一旦添加后,这个对象的Realm
属性值不再为null
(事实上,它的实际值就是realm
),已经与Realm数据库进行了关联(从Realm数据库中加载到的数据也一样),需要使用realm.Write(Action)
来更新其Realm字段数据。
这里,realm
和mandUser.Realm
在这个示例中是同一个对象,我们可以使用realm.IsSameInstance(mandUser.Realm)
来对二者进行检测是否相同,如果检测结果为false
,那表示这二者不是关联的同一数据库,不能使用realm
来更新mandUser
,当然,你可以用mandUser.Realm.Write(Action)
来更新它自己,但注意mandUser.Realm
可能为null
,如果它尚未关联Realm数据库。
如果想通过一个对象来更新一条已有记录,这里有一个方法Realm.Add(RealmObject, bool)
。这个方法的主要功能显然是用来向数据库添加记录的,不过它的第二参数是表示如果记录已经存在是否更新它
,这个方法的第一参数是一个RealmObject
对象,这个对象并不要求它已经关联了数据库,也就是它的Realm
属性值可以是null
,这个时候,如果要用它去更新数据库中已有的记录,就要设置这个对象的标记为[Realms.PrimaryKey]
属性的Realm字段,让这个字段的值与数据中的要更新的记录中的该字段值保持一致,这样在更新的时候(二参为true
),就会在数据库中找到并更新该条记录,如前文的示例:
realm.Write(() => { mandUser.Sex = "男"; //更新mandUser //因为这里的new User的Id=3,mandUser的Id也是3,Id是PrimaryKey,所以这里仍然更新mandUser realm.Add<User>(new User() { Id = 3, Name = "鳗驼螺", Sex = "Male", Age =
作者:鳗驼螺
链接:https://www.jianshu.com/p/24b96b496777
热门评论
大神 我用c#碰到No RealmObjects. Has linker stripped them? 这个问题,但是我有写子类继承RealmObject 可以帮我解答下吗?