我正在通过实施一个简单的财务簿记工具来尝试Realm.我正在使用类似于以下内容的模式(从基于sql的项目继承)
最初,我使用类型为long的PrimaryKey为所有这些表设置类.然后我了解到,至少截至2015年,领域doesn’t support auto-increment.在我自己的测试中,它似乎是真的,因为我得到一个例外,说0是一个重复的主键.
看一下getting started guide上的例子,我试图重新评估我是否真的需要特定的Id字段.在大多数情况下,除了在数据库中使用主键的潜在速度优势之外,我似乎并不特别做.但是,当我设置关系时,我认为Realm在幕后做这件事似乎是合理的.但有一个例外.
// TransactionLink.cs
using Realms;
namespace Finance.Models {
public class TransactionLink : RealmObject {
public Transaction Lhs { get; set; }
public Transaction Rhs { get; set; }
}
}
// Transaction.cs
using Realms;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Finance.Models {
public class Transaction : RealmObject {
[PrimaryKey]
public long Id { get; set; }
// ...
[Ignored]
public IQueryable<Transaction> LinkedTransactions {
get {
var lhs = Realm.All<TransactionLink>().Where(tl => tl.Lhs.Id == Id).Select(tl => tl.Rhs);
var rhs = Realm.All<TransactionLink>().Where(tl => tl.Rhs.Id == Id).Select(tl => tl.Lhs);
return lhs.Union(rhs);
}
}
}
}
起初我认为可能有交易有一个字段IList< Transaction>但是我不喜欢将一个条目添加到它们中的删除的好处.必须维护单独的数据副本对我没有吸引力.我考虑过而不是使用Equals函数比较Id,因为我注意到RealmObject重载它(类似于tl => tl.Rhs.Equals(this)),但我不确定它将如何处理查询(也许我应该在发布这个问题之后尝试一下……但是我可以通过一些方法来测试它
好吧,在这一点上(我可能是错的)似乎我最好实现一些自动递增的东西.因此,在afore-mentioned stack overflow post中的示例之后,我提出了一些建议.
// IPrimaryKeyId.cs
namespace Finance.Context {
public interface IPrimaryKeyId {
long Id { get; set; }
}
}
// Database.cs
using Finance.Models;
using Realms;
using System.Linq;
using System.Windows;
using static Finance.Context.Handle;
namespace Finance.Context {
public class Database {
//-------------------------------------------------------------------------------------------------------------+
// Non-Static Interface: Provide access to all the tables in the database |
//-------------------------------------------------------------------------------------------------------------+
private Realm Db { get; set; }
public IQueryable<Bank> Banks { get { return Db.All<Bank>(); } }
public IQueryable<Models.Transaction> Transactions { get { return Db.All<Models.Transaction>(); } }
public IQueryable<TransactionLink> TransactionLinks { get { return Db.All<TransactionLink>(); } }
// ...
public void SetupExample() {
AutoIncrement(new Bank { Name = "Cash" });
var first = Banks.First();
MessageBox.Show(first.Id.ToString() + ": " + first.Name);
}
public void AutoIncrement<T>(T obj) where T : RealmObject, IPrimaryKeyId {
AutoIncrement(Db, obj);
}
public static void AutoIncrement<T>(Realm db, T obj) where T : RealmObject, IPrimaryKeyId {
db.Write(() => {
long id = db.All<T>().Max(el => el.Id);
obj.Id = id + 1;
db.Add(obj);
});
}
}
}
afore-mentioned guide说领域不喜欢这些类中的继承,但它似乎可以通过这个简单的界面运行.问题是在调用Max()的行上会抛出NotSupportedException.所以现在我有一些潜在的问题要解决,其中任何一个都会让我前进.
> Max()的替代方法,用于确定最大Id
>验证在Linq查询中使用Equals(this)和放弃Ids的效果
>我没有考虑的另一种选择
这对我来说已经习以为常了,所以我想尽可能地做“事情方式”.关于如何更好地适应这种风格的任何其他评论都是受欢迎的.
对@Cristo的回应
调用异常的行是long id = db.All< T>().Max(el => el.Id);正如您在上面的代码片段中看到的那样.确切的文本是System.NotSupportedException:’不支持’Max’方法’.堆栈跟踪是
[External Code]
Finance.exe!Finance.Context.Database.AutoIncrement.AnonymousMethod__0() Line 48 C#
[External Code]
Finance.exe!Finance.Context.Database.AutoIncrement<Finance.Context.Handle>(Realms.Realm db, Finance.Context.Handle obj) Line 47 C#
Finance.exe!Finance.Context.Database.Create(string file) Line 74 C#
Finance.exe!Finance.MainWindow.Button_Click(object sender, System.Windows.RoutedEventArgs e) Line 14 C#
[External Code]
其中第47行对应于db.Write(而48对应于调用Max的行
解决方法:
The problem is that on the line that calls Max() it throws a NotSupportedException
假设你有一个int | long | …类型作为主键,你可以在瞬态查询上使用Last()来获取最后一个| max id值并手动将其递增一个(因为Realm查询是延迟加载的,只有最后一个对象被实例化以确定主键的当前值.
例:
public class ItemModel : RealmObject
{
public ItemModel() { }
public ItemModel(int ID)
{
this.ID = ID;
}
[PrimaryKey]
public int ID { get; set; }
public static ItemModel Create()
{
ItemModel NewItemModel()
{
var q = Realm.GetInstance(Consts.realmDBName).All<ItemModel>();
return new ItemModel(q.Any() ? q.Last().ID + 1 : 0);
}
if (Realm.GetInstance(Consts.realmDBName).IsInTransaction)
{
return NewItemModel();
}
// Use a pseudo transaction to ensure multi-process safety on obtaining the last record
using (var trans = Realm.GetInstance(Consts.realmDBName).BeginWrite())
{
return NewItemModel();
}
}
}
用法:
var aRealmObject = instance.Add(ItemModel.Create(), false);