对于我们的 Web 应用程序 (ASP.NET),我们使用 Fluent NHibernate (2.1.2) 和二级缓存,不仅用于实体,还用于查询(使用标准 API 生成查询)。我们使用 Session-Per-Request 模式和一个 SessionFactory 应用程序,因此缓存服务于所有 Nhibernate-Sessions。
问题:
我们必须在遗留数据库 (Oracle) 中的数据对象上为每个用户处理不同的“访问权限”——也就是说,视图限制了每个用户权限的返回数据。所以有这样的情况,例如,我们的条件使用完全相同的查询查询相同的视图,但返回不同的结果集,具体取决于用户权限。
现在,为了获得性能,提到的查询被缓存。但这给我们带来了问题,当用户 A 的操作首次触发查询时,它会缓存结果 ID,即用户 A 有权访问的 ID。不久之后,用户 B 的操作触发了相同的查询,然后 Nhibernate 从第一次调用(来自用户 A)中选择缓存的 ID,并尝试获取用户 B 没有访问权限的相应实体(或者可能不适合所有人)。我们正在使用事件侦听器检查权限,因此我们的应用程序在上述情况下会引发访问权限异常。
想法:
不缓存查询可能是针对此的一种选择。但是性能在我们的应用程序中显然是一个问题,因此非常希望缓存用户的查询。
我们甚至考虑过每个用户都有一个 SessionFactory,每个用户都有一个缓存,有点。但这显然对资源有影响,有点矫枉过正,老实说不是一个选择,因为有实体,必须由多个用户(想想一个用户组)访问和操作,创建“个人缓存”中的陈旧数据等问题。所以这是不行的。
什么是有效的解决方案?对于这种情况,是否有“最佳实践”之类的东西?
主意:
因为我昨天被困在这个问题上,看不到出路,所以我睡在上面,今天我想出了某种“黑客”。
由于 NHibernate 通过查询文本和参数(“子句”)缓存查询,我想到了一种方法,在查询的签名中“走私”与用户相关的东西,因此它会缓存每个用户的每个查询,但不会更改查询本身(关于查询结果)。
所以“创造力”引导我这样做(示例代码):
string userName = GetCurrentUser();
ICriteria criteria = session.CreateCriteria(typeof (EntityType))
.SetCacheable(true)
.SetCacheMode(CacheMode.Normal)
.Add(Expression.Eq("PropertyA", 1))
.Add(Expression.IsNotNull("PropertyB"))
.Add(Expression.Sql(string.Format("'{0}' = '{0}'", userName)));
return criteria.List();
这一行:
.Add(Expression.Sql(string.Format("{0} = {0}", userName)))
导致 where 子句,它总是评估为 true,但从 Nhibernate 的角度“更改”查询,因此它缓存每个单独的“用户名”。
我知道,这有点难看,我对此并不满意。 有人知道任何替代方法吗?
提前致谢。