用于有状态应用程序的 ORM。 EF适合吗?或者任何?
Posted
技术标签:
【中文标题】用于有状态应用程序的 ORM。 EF适合吗?或者任何?【英文标题】:ORM for stateful application. Does EF fit? Or any? 【发布时间】:2015-12-17 18:08:37 【问题描述】:我需要一个适合有状态应用的 ORM。我将在具有持久客户端连接的低延迟实时游戏服务器中保持请求之间的实体。只有一个服务器实例连接到数据库,因此不能从“外部”更改任何数据,服务器可以依赖其缓存。
当用户远程登录到服务器时,它的整个配置文件被加载到服务器内存中。还为每个用户创建了几个更高级别的服务来操作配置文件数据并提供功能。它们还可以具有内部字段(状态)来存储临时数据。当用户想要更改他的签名时,他要求相应的服务这样做。该服务跟踪用户更改其签名的频率,并且每十分钟仅允许一次(例如) - 数据库中不会跟踪如此短的间隔,这是一种临时状态。此更改应存储到仅执行 1 个查询的 db:UPDATE users SET signature = ... WHERE user_id = ...
。当用户注销时,它会在数分钟/数小时不活动后从服务器内存中卸载。这里的db只是一个存储。这就是我所说的有状态。
-
某些实体被认为是“静态数据”,仅在应用程序启动时加载一次。这些可以从其他“动态”实体中引用。加载“动态”实体不应要求重新加载引用的“静态数据”实体。
Update
/Insert
/Delete
应该只设置/插入/删除更改的属性/实体,即使是“分离”实体。
写入操作不应每次从数据库加载数据(执行Select
)初步检测更改。 (可以在动态生成的继承者中跟踪状态。)我在本地有一个状态,加载任何东西都没有意义。 我想继续跟踪连接范围之外的更改,并在需要时“上传”更改。
在执行操作时,不应更改持久对象的引用。
DBConnection-per-user 无法正常工作。预计在线用户数以千计。
“静态数据”中的实体可以分配给“动态”实体属性(代表外键),Update
应该正确处理它。
现在我正在使用 NHibernate,尽管它是为无状态应用程序设计的。它支持重新附加到会话,但看起来非常不常见,需要我使用未记录的行为并且不能解决所有问题。
我不确定 Entity Framework - 我可以那样使用它吗?或者你能推荐另一个 ORM 吗?
如果服务器在每次用户点击按钮时重新创建(或特别是重新加载)用户对象,它将非常快速地占用 CPU。 CPU 垂直扩展的成本很高,但影响很小。相反,如果你的 RAM 用完了,你可以去购买更多——比如水平缩放,但更容易编码。如果您认为应该在这里使用另一种方法,我准备讨论它。
【问题讨论】:
不知道你为什么说 Nhibernate 是为无状态应用程序设计的。 NH 的会话对我来说似乎很有状态。 两种适用架构之间没有太大区别。但是这个问题太宽泛且基于意见,赏金并不能解决这个问题。而且,关于 session.Merge、SaveOrUpdate 等有什么不常见和未记录的? @GertArnold 那些方法使用 Select,见 #3。我必须使用具有一些未记录行为的 Session.Lock(重新附加、执行更改和提交)(当我调用它两次时,它允许我忽略一些检查 - 只有第一次抛出)。 情况正好相反。会话设计用于有状态的场景,而不是“在短时间内”使用。当然在 web 等无状态环境中,每个请求绑定一个 session,但是 session 适合桌面场景。换句话说,会话设计并不假定它的用途。 @SimonMourier 请参阅***.com/questions/3754592/… 最佳答案“请记住,NH 并非旨在与长寿命的 ISession 一起使用”。传递物体也有困难,例如当我有来自一个会话的“静态对象”和来自另一个会话的“动态对象”时。 【参考方案1】:我在一个有状态的桌面应用程序中使用了休眠,它的会话非常长:会话在应用程序启动时开始,并在应用程序运行期间保持打开状态。我对此没有任何问题。我完全不使用附加、分离、重新附加等。我知道这不是标准做法,但这并不意味着它不可行,或者存在任何陷阱。 (编辑:但当然要阅读下面的讨论,了解其他人提出的可能存在的陷阱。)
我什至在此基础上实现了自己的更改通知机制(单独的线程直接轮询数据库,绕过休眠),因此甚至可以让外部代理在休眠运行时修改数据库,并让您的应用程序注意到这些变化。
如果你有很多东西已经在使用hibernate,那么放弃你已经拥有的东西并重写它可能不是一个好主意,除非你确定hibernate绝对不会做你想要完成的事情。
【讨论】:
"DBConnection-per-user 无法正常工作。预计在线用户数为数千。"我也不能对整个多线程应用程序使用单个会话。当缓存时间到期时,我需要从内存中卸载用户(以及所有相应的实体)。 您不必为每个用户使用一个数据库连接,事实上我什至不认为您可以,因为这意味着为每个活动用户保留一个SessionManager
实例化。你只需要在你的应用程序和hibernate之间实现一个层,这样所有用户都可以访问SessionManager
的同一个实例。如果这不是一个选项,那么您需要根据需要实例化会话管理器,并进行大量附加和分离,但据我所知,这对于休眠也是完全可行的。这正是它支持附加和分离的原因。
SessionManager 是指 ISessionFactory 吗?您是否知道每个 ISession(至少一次用于查询任何内容)都使用单独的数据库连接?
您可以在 N 个线程上运行 N 个休眠会话,从队列中获取消息,从而实现 N 个并行化。其中 N 类似于 10。
@MikeNakis:每个应用一个会话仅适用于相对较小的数据库或用户会话较短的情况。当数据库很大和/或用户在单个会话中工作很长时间时,用户使用应用程序的时间越长,一级缓存就越大。迟早它会消耗所有内存并且应用程序变得无响应。当你谈到潜在的陷阱时,应该提到这一点。【参考方案2】:
是的,您可以将 EF 用于此类应用程序。请记住,在重负载时,您有时会遇到一些数据库错误。通常,当您跟踪更改而不是 EF 时,错误后恢复会更快。顺便说一下,你也可以用这种方式NHibernate。
【讨论】:
你能澄清一下我可以使用 NHibernate 的确切方式吗?对于 EF - 有没有这种用法的例子?我认为 EF 将实体严格附加到其 DbContext 与 NH 与 ISession 的方式相同。 默认情况下 EF 将实体附加到会话。要禁用此行为,您可以使用 .AsNoTracking()。对于 NHibernate,它可以是 session.Evict(yourObj)。 但是当我需要UPDATE
时它会如何检测更改?做另一个SELECT
?这是我想要避免的。
Merge
是一个选项,但比较所有内容,每个“刷新”的每个集合和子集合元素也不是很好。以上是关于用于有状态应用程序的 ORM。 EF适合吗?或者任何?的主要内容,如果未能解决你的问题,请参考以下文章