entity-framework - 在 RavenDB 集合中存储/查询多种类型的最佳方法是什么?

标签 entity-framework logging nosql relational-database ravendb

我正在设计一个日志系统,将其日志条目存储在 RavenDB 中,对于这个特定系统,我想存储(以及稍后查询)具有基于所记录事件类型的不同数据结构的文档。考虑我可能想要记录的以下事件:

  • 用户登录 - 存储用户 ID
  • 用户删除文件 - 存储用户 ID 和正在删除的文件名

  • 我有几种不同的方式可以去这里......

    选项 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/

    相关文章:

    c# - SQL 中的数据分组

    c# - Entity Framework 如何按导航属性的属性过滤我的结果?

    sql-server - EF ObjectQuery<T> 上下文、参数、连接属性等效于 DbSet<T>

    google-app-engine - 如何在 App Engine Go 日志中自动记录函数/行号?

    MongoDb 替换文档并在不存在时插入

    c# - Entity Framework 核心连接字符串 - 环境变量

    azure - ASP.NET Core 3.1 无法在 Azure 服务日志流上查看我的日志

    c++ - PCAP捕获文件头怎么写?

    nosql - Redis 和 Membase 之间的主要区别是什么?

    mysql - 使用两个不同的数据库来复制数据并根据适合每个数据库的数据以最高效的方式提供读取服务