我正在设计一个将日志条目存储在RavenDB中的日志记录系统,对于这个特定的系统,我想根据记录的事件类型存储(以及稍后查询)具有不同数据结构的文档。请考虑以下我可能要记录的事件:
我有几种不同的方式可以去这里......
class LoginEvent
{
public int UserId { get; set; }
}
class FileDeleteEvent
{
public int UserId { get; set; }
public string Filename { get; set; }
}
这种方法在RavenDB中产生了两个不同的集合,并且它们很容易查询。但是,检索所有日志条目的并集需要多次查询和多次往返服务器 - 一次用于LoginEvents,另一次用于FileDeleteEvents。只有两种事件类型没有太大区别,但随着事件类型数量的增加,问题会变得更加严重。
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.
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属性,该类提供特定于事件类型的详细信息。起初,这个选项似乎是最好的,因为它将所有日志条目存储在单个Event集合中,并且可以轻松查询完整集合。但是,假设我只想要Filename为“test.txt”的FileDelete事件。这变得有点棘手。
例如,以下内容引发了一个有点模糊的错误,即“Filename”字段没有被编入索引:
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();
因此,尽管从数据存储的角度来看这个选项可能很好,但从可查询性的角度来看却失败了。 (假设我正在构建正确的查询 - 可能还有另一种我不考虑的方式。)
enum EventType { Login, FileDelete }
class Event
{
public EventType EventType { get; set; }
public int UserId { get; set; }
public string Filename { get; set; }
.
.
.
}
这种方法虽然从存储角度来看有点浪费,但从可查询性的角度来看却是微不足道的。当您开始添加要记录的更多类型的事件时会出现问题 - 然后属性的数量开始增加。
我可以相当简单地使用EF的table-per继承模式进行有效的查询。这种方法的缺点是Sql对于这个问题严重过度 - 我们不需要数据一致性和关系系统提供的其他严格性。而且,根据我的经验,Sql插件比RavenDB中的文档存储要慢得多(对日志系统来说是一个重要的考虑因素)。
所以,有选择......你怎么看?我错过了什么吗?
答案 0 :(得分:5)
解决此问题的“官方”方式似乎是多态指数: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
答案 1 :(得分:2)
使用基类的东西。诀窍是使用多态并将所有具体类型设置为使用相同的类型标记名称。现在,您可以轻松查询它们,因为它们位于同一个集合中。
FindTypeTagName = type =>
{
if (typeof (LoginEvent).IsAssignableFrom(type) ||
typeof (FileDeleteEvent).IsAssignableFrom(type))
return "event";
return DocumentConvention.DefaultTypeTagName(type);
}
答案 2 :(得分:0)
基础课程。总是尝试使用适当的oop。
您需要指定所有子类应存储在同一个集合中