【发布时间】:2014-11-11 09:45:34
【问题描述】:
我开发了一个具有持久客户端连接(非基于请求)的服务器。当我在内存中跟踪每个连接的客户端状态时,如果我每次需要访问此类客户端数据时都加载实体会很奇怪。
所以我有我的分离实体,当我需要执行任何更改时,我不会直接应用它们,而是将这些更改和分离实体作为请求传递给 GameDb 类。它对此实体执行更改,然后从数据库加载相同的实体以再次对会话拥有的实体执行相同的更改,以便 NH 可以跟踪这些更改。
我可以使用 Merge 但它要慢得多,因为 NH 应该加载所有实体数据(包括可能未修改的惰性集合)来检查每个属性的更改。就我而言,性能至关重要。
一个例子:
public void GameDb.UpdateTradeOperation(UserOperation operation, int incomeQuantity, decimal price)
{
if (operation == null) throw new ArgumentNullException("operation");
if (operation.Id == 0) throw new ArgumentException("operation is not persisted");
_operationLogic.UpdateTradeOperation(operation, incomeQuantity, price);
try
{
_factory.Execute(
s =>
{
var op = s.Get<UserOperation>(operation.Id);
_operationLogic.UpdateTradeOperation(op, incomeQuantity, price);
if (op.User.BalanceFrozen != operation.User.BalanceFrozen)
throw new Exception("Inconsistent balance");
}); // commits transaction if no exceptions thrown
}
catch (Exception e)
{
throw new UserStateCorruptedException(operation.User, null, e);
}
}
这种方法带来了一些过于复杂的问题,因为我需要两次应用每个更改并检查结果状态是否相等。如果我可以使用 NH Session 来监控实体更改,那就更容易了。但不建议长时间保持 NH 会话打开,我可能会有数千个这样长时间打开的会话。
它还迫使我拆分我的实体和通用逻辑。问题是 GameDb 类不知道它是从哪个上下文调用的,并且不能为其操作请求任何附加数据(例如当前价格或客户端套接字非活动计时器或许多其他东西),或者它可能需要有条件地(由其决定) 向客户端发送一些数据。当然,我可以将一堆委托传递给 GameDb 方法,但在我看来这不是一个好的解决方案。
我可以使用 Session.Lock 来附加我未更改的分离实体,这样我就不需要执行两次更改了吗?我应该使用什么 LockMode?p>
我可以在这里使用更好的方法吗?如果我为每个客户端保留一个打开的会话但快速提交或回滚事务,它仍然会打开很多连接吗?事务完成后会话会保持实体状态吗?
长期存在的每个客户端会话可能会遇到什么样的并发问题:
- 如果我只从自己的线程光纤(或锁)操作每个用户实体?
- 如果我从“错误”会话(来自该会话的线程)请求另一个只读用户配置文件?
【问题讨论】:
标签: c# nhibernate