如何在具有自己用户类型的延迟加载属性上激活二级缓存

本文关键字:延迟加载 属性 二级缓存 激活 类型 用户 自己 | 更新日期: 2023-09-27 18:14:13

前言
在我的应用程序中,我将原始WAV数据作为byte[]存储在数据库中。在我的域模型中,有一个类PcmAudioStream表示原始WAV数据。我创建了NHibernate的IUserType的实现,以便在我的类和byte[]之间进行转换
有几个类使用PcmAudioStream类,所有这些类都映射到数据库表。为了避免在从这样的表中检索行时总是加载所有WAV数据,我创建了Fluent NHibernate的IUserTypeConvention的实现,该实现指定这些属性应该始终延迟加载
所有这些都很有魅力。

问题:
因为这些PcmAudioStream的内容很少更改,所以我想将检索到的实例放在二级缓存中。现在,我知道如何为一个完整的类激活二级缓存,但如何仅为懒惰加载的属性实现这一点?


我的域模型的相关部分如下:

public class User : Entity
{
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual PcmAudioStream FullNameRecording { get; set; }
    // ...
}

映射很简单(注意:这不是我的映射,我使用的是一个约定,但它是等效的(:

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.Id);
        Map(x => x.FirstName);
        Map(x => x.LastName);
        Map(x => x.FullNameRecording).CustomType<PcmAudioStreamAsByteArray>();
    }
}

如何在具有自己用户类型的延迟加载属性上激活二级缓存

您可以使用专用静态缓存来实现这一点。这需要做更多的设置工作,但不需要对域模型进行额外的类或公共更改。一个很大的缺点是,条目不会从缓存中删除,但您可以使用自定义集合或"全局"缓存来限制条目的数量。

public class Entity
{
    public virtual int Id { get; protected set; }
}
public class PcmAudioStream
{}
public class User : Entity
{
    private static readonly IDictionary<int, PcmAudioStream> _fullNameRecordingCache;
    private PcmAudioStream _fullNameRecording;
    static User()
    {
        _fullNameRecordingCache = new Dictionary<int, PcmAudioStream>();
    }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual PcmAudioStream FullNameRecording
    {
        get
        {
            if (_fullNameRecordingCache.ContainsKey(Id))
            {
                return _fullNameRecordingCache[Id];
            }
            // May need to watch for proxies here
            _fullNameRecordingCache.Add(Id, _fullNameRecording);
            return _fullNameRecording;
        }
        set
        {
            if (_fullNameRecordingCache.ContainsKey(Id))
            {
                _fullNameRecordingCache[Id] = value;
            }
            _fullNameRecording = value;
        }
    }
    // ...
}

映射:

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.Id);
        Map(x => x.FirstName);
        Map(x => x.LastName);
        Map(x => x.FullNameRecording).CustomType<PcmAudioStreamAsByteArray>()
            .Access.CamelCaseField(Prefix.Underscore);
    }
}

根据评论编辑:

我认为在用户类型中不可能实现这一点,因为IDataReader已经在NullSafeGet中打开。我认为您可以在实现IPreLoadEventListener的侦听器中执行此操作,但这不允许您使缓存无效。我认为这两种选择都不可行。

经过进一步思考,我仍然认为我的原始解决方案(或变体(是最好的选择。我理解(并同意(您对干净域模型的渴望,但有时妥协是必要的,我的解决方案不会改变模型的公共成员,也不需要任何额外的引用。另一个理由是,对象是第一个知道记录已更改,需要在缓存中替换或添加到缓存的对象。

我不确定是否只缓存单个属性,但我的猜测是NH缓存基础设施不是这样构建的。IMHO您可以将整个类实例或查询结果放入二级缓存。

但我会尝试草拟一个解决方案。

在NH3和对懒惰属性的支持之前,如果你不想从DB加载整个实体(在你的情况下,这完全有意义!(,你必须将这样"昂贵"的数据保存在一个引用的、懒惰加载的表中。至少我是这样解决的。

这看起来像是后退了一步,但使用这种方法,我非常确信您将能够缓存这些数据。

另一方面,NH3+中的缓存和QueryOver似乎存在问题:https://nhibernate.jira.com/browse/NH-2740