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

时间:2011-11-12 20:39:24

标签: c# .net nhibernate caching fluent-nhibernate

前言
在我的应用程序中,我将原始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>();
    }
}

2 个答案:

答案 0 :(得分:2)

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

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的侦听器中执行此操作,但这不允许您使缓存无效。我认为这两种选择都不可行。

在考虑了一些之后,我仍然认为我的原始解决方案(或变体)是最佳选择。我理解(和分享)您对干净的域模型的渴望,但有时需要妥协,我的解决方案不会更改模型的公共成员或需要任何其他参考。另一个理由是该对象是第一个知道记录已经改变并且需要替换或添加到缓存中的对象。

答案 1 :(得分:1)

我不确定只缓存一个属性,但我的猜测是,这不是NH缓存基础架构的构建方式。恕我直言,您可以将整个类实例或查询结果放入二级缓存。

但我会尝试勾勒出一个解决方案。

在NH 3之前以及对延迟属性的支持,如果你不想从数据库中加载整个实体(在你的情况下,这非常有意义!)你必须将这些“昂贵”的数据保存在引用,延迟加载表。至少那是我解决它的方式。

这似乎是后退一步,但使用这种方法,我很确定你能够缓存这些数据。

单独注意,NH3 +中的缓存和QueryOver似乎存在问题:https://nhibernate.jira.com/browse/NH-2740

相关问题