我正在设计一个日志系统,将其日志条目存储在 RavenDB 中,对于这个特定系统,我想存储(以及稍后查询)具有基于所记录事件类型的不同数据结构的文档。考虑我可能想要记录的以下事件:
我有几种不同的方式可以去这里......
选项 A. 创建两种完全不同的类型
class LoginEvent
{
public int UserId { get; set; }
}
class FileDeleteEvent
{
public int UserId { get; set; }
public string Filename { get; set; }
}
这种方法会在 RavenDB 中产生两个不同的集合,它们很容易查询。然而,检索所有日志条目的联合需要多次查询和多次往返服务器——一次用于 LoginEvents,第二次用于 FileDeleteEvents。只有两种事件类型并没有太大区别,但随着事件类型数量的增加,问题会变得更糟。
选项 B. 创建一个基类并从中派生
abstract class Event
{
}
class LoginEvent : Event
{
public int UserId { get; set; }
}
class FileDeleteEvent : Event
{
public int UserId { get; set; }
public string Filename { get; set; }
}
我尝试过这种方法,但 RavenDB 似乎是按实际类型而不是强制类型来存储和查询文档——当我这样做时
Query<Event>().ToArray()
我得到了零结果。为了取回文档,我必须查询它们各自的类型,这实际上相当于上面的选项 A。选项 C. 创建不同的属性类
enum EventType { Login, FileDelete }
class Event
{
public EventType EventType { get; set; }
public object Info { get; set; }
}
class LoginInfo
{
public int UserId { get; set; }
}
class FileDeleteInfo
{
public int UserId { get; set; }
public string Filename { get; set; }
}
使用这种方法,我们总是存储一个 Event 类型的条目,但我们使用相应的 Info 类填充其 Info 属性,该类提供特定于事件类型的详细信息。起初,这个选项似乎是最好的,因为它将所有日志条目存储在单个事件集合中,并使查询完整集合变得容易。但是,假设我只想要文件名为“test.txt”的 FileDelete 事件。这变得有点棘手。
例如,下面会抛出一个关于“文件名”字段未被索引的有点模糊的错误:
var events = session.Query<Event>()
.Where(a => a.EventType == EventType.FileDelete)
.Where(a => ((FileDeleteInfo)a.Info).Filename == "test.txt")
.ToArray();
以下,除了不是我想要的,返回零结果:
var events = session.Query<Event>()
.Select(a => a.Info)
.OfType<FileDeleteInfo>()
.Where(a => a.Filename == "test.txt")
.ToArray();
事实上,下面的投影,根据文档支持的操作,甚至没有返回预期的类型,只是一堆没有意义的奇怪的中间结果:
var events = session.Query<Event>()
.Select(a => a.Info)
.ToArray();
因此,虽然这个选项从数据存储的角度来看可能是好的,但从可查询性的角度来看却失败了。 (假设我正在构建正确的查询 - 可能还有另一种我没有考虑的方式)。
选项 D. 创建一个具有所有可能属性的巨型事件类
enum EventType { Login, FileDelete }
class Event
{
public EventType EventType { get; set; }
public int UserId { get; set; }
public string Filename { get; set; }
.
.
.
}
这种方法虽然从存储的角度来看有点浪费,但从可查询性的角度来看是微不足道的。当您开始添加更多类型的要记录的事件时会出现问题 - 然后属性的数量开始增加。
选项 E. 忘记 RavenDB 并使用 Entity Framework + Sql
我可以相当简单地做到这一点,并使用 EF 的 table-per 继承模式有效地查询。这种方法的缺点是 Sql 对于这个问题来说是严重的矫枉过正——我们不需要关系系统提供的数据一致性和其他严格性。而且,根据我的经验,Sql 插入比将文档存储到 RavenDB(日志系统的一个重要考虑因素)要慢得多。
所以,有选择......你怎么看?有什么我错过的吗?
可能相关:Specifying Collection Name in RavenDB
最佳答案
解决这个问题的“官方”方法似乎是多态索引:https://ravendb.net/docs/article-page/3.0/csharp/indexes/indexing-polymorphic-data
这是详细讨论这种方法的博客条目:http://www.philliphaydon.com/2011/12/14/ravendb-inheritance-revisited/
这里还有一个视频:http://youtu.be/uk2TVs-d6sg
关于entity-framework - 在 RavenDB 集合中存储/查询多种类型的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9441831/