NHibernate ReadWriteCache,版本控制和父/子映射

时间:2012-01-11 07:29:24

标签: c# nhibernate

简要说明: 在父级及其子级之一更新后,事务提交,会话关闭,下一级会话从L2缓存获取父级,但更新的子级从db加载。家长是版本的,孩子不是。

详细说明: 正如我所提到的,我有父母与儿童收藏。父版本已经版本化,但子版本不是我的应用程序,映射保证所有更新都通过保存父级来完成。我看到当我加载带子项的父项时,父项和大多数子项都是从L2缓存加载的,但最近更新的子项是从数据库加载的。我在ReadWriteCache细节上做了一些研究,发现在保存父和更新的子代之后,下面的代码决定不更新缓存:

public bool Put(CacheKey key, object value, long txTimestamp, object version, IComparer versionComparator, bool minimalPut)
{
    bool CS$1$0000;
    if (txTimestamp == -9223372036854775808L)
    {
        return false;
    }
    lock (this._lockObject)
    {
        if (log.IsDebugEnabled)
        {
            log.Debug("Caching: " + key);
        }
        try
        {
            this.cache.Lock(key);
            ILockable lockable = (ILockable) this.cache.Get(key);
            if ((lockable == null) || lockable.IsPuttable(txTimestamp, version, versionComparator))
            {
                this.cache.Put(key, new CachedItem(value, this.cache.NextTimestamp(), version));
                if (log.IsDebugEnabled)
                {
                    log.Debug("Cached: " + key);
                }
                return true;
            }
            if (log.IsDebugEnabled)
            {
                if (lockable.IsLock)
                {
                    log.Debug("Item was locked: " + key);
                }
                else
                {
                    log.Debug("Item was already cached: " + key);
                }
            }
            CS$1$0000 = false;
        }
        finally
        {
            this.cache.Unlock(key);
        }
    }
    return CS$1$0000;
}

在我的情况下,lockable不为null,但它不可放置:

public bool IsPuttable(long txTimestamp, object newVersion, IComparer comparator)
{
    // we really could refresh the item if it  
    // is not a lock, but it might be slower
    //return freshTimestamp < txTimestamp
    return version != null && comparator.Compare(version, newVersion) < 0;
}

从NHibernate源代码可以看出,早期的决策是通过比较时间戳来制定的,但现在版本开始发挥作用。

现在的问题是:我应该在我的域模型中对每个实体进行版本化吗?在问题出现之前,我确信我应该只对Aggregate Roots进行版本化。

我真的希望找到解决这个问题的另一种方法,因为我的父子层次结构要深得多(不要问为什么:)),让我们说它深层次3级(实际上它更深): 根 - &GT;儿童安全&GT;如GrandChild

在仅更新GrandChild的情况下,我预计会有2个UPDATE语句:一个用于GrandChild,另一个用于Root以更新其版本。但是,如果我版本Child也会花费额外的数据库更新和往返:( ...顺便说一句,应该ping Ayende或来自NH的人 - ADO.NET批处理NH只适用于相同的实体,它不会放在同一个GrandChild,Child和Root的批量更新语句。

2 个答案:

答案 0 :(得分:1)

您还需要在子实体上设置版本属性,而不仅仅是聚合根。我在我当前的项目中有一个类似的用例,这就是它的工作方式。我们通过实现实体的基类中的版本和流畅的nhibernate映射类的基类来管理它。

答案 1 :(得分:1)

对不起,伙计们,让你感到困惑。现在看来我已经明白了。让我们来看看ReadWriteCache类(实际上是一个策略,而不是它自己的缓存)。我们对三种方法感兴趣:Put(我在我的问题中提到过),AfterInsert和AfterUpdate:

  • AfterInsert决定我们应该将插入的实体存储在L2缓存中。这就像是“直写”行为。如果使用身份生成器,则不会调用它:(
  • AfterUpdate决定我们应该在L2中存储更新的实体。在非并发环境中,它取决于

    public bool IsCacheInvalidationRequired
    {
        get { return HasFormulaProperties || (!IsVersioned && (entityMetamodel.IsDynamicUpdate || TableSpan > 1)); }
    }
    
  • 最后让我们谈谈Put。我们决定是否应该从L2缓存中的数据库实体存储加载。可能发生以下情况:a)如果L2没有这个(通过id)实体,我们总是存储加载的实体b)如果L2已经有这个实体,那么我们只在L2 中更新它如果实体是版本化的它的版本比现有版本更新。

希望现在我真的明白,当我创建或更新的实体出现在L2缓存中时,为什么我会看到额外的db select语句。所有这些规则都适用于ReadWriteCache。可能有人使用NonStrictReadWriteCache,但这不是我的选择。