NHibernate SysCache не кэширует дочерние объекты

Надеюсь на вашу помощь. У меня есть простые наборы master-child ("player" и "playerChildObject"). Выбор "игроков" также выбирает их связанных детей.

SysCache кэширует игроков, но не кэширует детей.

Вот объекты (оба установлены с помощью Cache.ReadWrite()):

Объекты:

public class Player
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<PlayerChildObject> PlayerChildObjects { get; set; }

    public Player()
    {
        PlayerChildObjects = new List<PlayerChildObject>();
    }
}

public class PlayerChildObject
{
    public virtual int Id { get; protected set; }
    public virtual Player Player { get; set; }
}

Отображение:

public class PlayerMap : ClassMap<Player>
{
    public PlayerMap()
    {
        Id(x => x.Id).Column("id");
        Map(x => x.Name).Column("name");
        HasMany(x => x.PlayerChildObjects).KeyColumn("PlayerId")
            .Inverse()
            .Cascade.All()
            .Not.LazyLoad();
        Table("accounts");
        Cache.ReadWrite();
    }
}

public class PlayerChildObjectMap : ClassMap<PlayerChildObject>
{
    public PlayerChildObjectMap()
    {
        Id(x => x.Id).Column("id");
        References<Player>(x => x.Player, "playerId");
        Table("playerChildObjects");
        Cache.ReadWrite();
    }
}

Фабрика сессий также с "UseQueryCache" и "UseSecondLevelCache":

Fluently.Configure()
.Database(MySQLConfiguration.Standard.ConnectionString(cs => ...))
.Cache(c => c.UseQueryCache().UseSecondLevelCache()
.ProviderClass(typeof (NHibernate.Caches.SysCache.SysCacheProvider).AssemblyQualifiedName))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<PlayerChildObjectMap>())
.BuildSessionFactory();

И сам запрос с помощью SetCacheable(true).SetCacheMode(CacheMode.Normal):

using (var session = SessionFactory.OpenSession())
{
    using (var transaction = session.BeginTransaction())
    {
        players = session.CreateCriteria(typeof(Player))
            .SetCacheable(true)
            .SetCacheMode(CacheMode.Normal)
            .List<Player>();
        transaction.Commit();
    }
}

Тем не менее, профилировщик показывает, что игроки кэшируются (вызывается только при первом выполнении кода получения игроков), в то время как одни и те же объекты PlayerChildObject извлекаются PlayerID каждый раз.

Итак, первый запрос дает N+1 вызовы БД (список игроков + дочерние объекты по каждому игроку), все последовательные запросы выполняют N вызовов (дочерние объекты по каждому игроку).

Что мне не хватает? Как сделать так, чтобы SysCache также кэшировал детей?

Версия SysCache: 3.1.0.4000

1 ответ

Решение

Я бы сказал, что есть две проблемы. Во-первых, даже one-to-many ( HasMany ) имеет настройку кеша:

HasMany(x => x.PlayerChildObjects).KeyColumn("PlayerId")
    ...
    .Cache.IncludeAll() // or .IncludeNonLazy, .CustomInclude("customInclude")
        .ReadOnly() // or .NonStrictReadWrite(), .ReadWrite(), .Transactional(),
                    //  .CustomUsage("customUsage")        
        .Region("regionName");

Узнайте больше о сопоставлении HasMany здесь (статья Адама Бара о сопоставлении по коду, но в конце - свободная)

Документ 19.2.1. Отображения кэша

Во-вторых, мы должны использовать пакетную загрузку:

public PlayerMap()
{
    BatchSize(25);

    HasMany(x => x.PlayerChildObjects).KeyColumn("PlayerId")
        ...
        .BatchSize(25)
...
public PlayerChildObjectMap()
{
    BatchSize(25);
    ...

Подробнее об этом читайте в документе 19.1.5. Используя пакетную загрузку, или здесь:

Другие вопросы по тегам