NHibernate кэш 2-го уровня не кэширует всю сущность - PullRequest
1 голос
/ 29 апреля 2011

У меня есть этот код:

[TestFixture]
public class CachingTest
{
    [Test]
    public void Caches_second_request()
    {
        var sessionFactory = CreateSessionFactory();

        int orderId;
        {
            var order = new Order {Created = DateTime.Now};
            order.Rows.Add(new OrderRow {Price = 123, Order = order});
            order.Rows.Add(new OrderRow { Price = 456, Order = order });

            using (var session = sessionFactory.OpenSession())
            {
                session.Save(order);
                orderId = order.Id;
            }
        }

        Console.WriteLine("Saved");

        using (var session = sessionFactory.OpenSession())
        {
            var order = session.Get<Order>(orderId);

            Console.WriteLine(order.Rows.Count);
        }

        Console.WriteLine("Fetched first time");

        using (var session = sessionFactory.OpenSession())
        {
            var order = session.Get<Order>(orderId);

            Console.WriteLine(order.Rows.Count);
        }
    }

    private static ISessionFactory CreateSessionFactory()
    {
        var autoMappingConfig = new AutoMappingConfiguration();

        ISessionFactory sessionFactory = Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2008
                          .ConnectionString(c => c.FromAppSetting("connectionString"))
                          .ShowSql())
            .Cache(c => c
                            .UseQueryCache()
                            .UseSecondLevelCache()
                            .ProviderClass<KamakuraCacheProvider>())
            .Mappings(m =>
                      m.AutoMappings.Add(
                          AutoMap.AssemblyOf<CachingTest>(autoMappingConfig)
                              .Conventions.Add(
                                  ForeignKey.EndsWith("Id"),
                                  DefaultLazy.Never(),
                                  DefaultCascade.All())
                              .Conventions.Add<CacheableConvention>()
                          ))
            .ExposeConfiguration(configuration =>
                                 new SchemaExport(configuration).Create(false, true))
            .BuildSessionFactory();

        return sessionFactory;
    }
}

public class CacheableConvention : IClassConventionAcceptance, IClassConvention
{
    public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
    {
        criteria.Expect(x => x.EntityType.IsAny(typeof (Order), typeof (OrderRow)));
    }

    public void Apply(IClassInstance instance)
    {
        instance.Cache.ReadWrite();
        instance.Cache.IncludeAll();
    }
}

public class AutoMappingConfiguration : DefaultAutomappingConfiguration
{
    public override bool ShouldMap(Type type)
    {
        return type == typeof (Order) || type == typeof (OrderRow);
    }

    public override Access GetAccessStrategyForReadOnlyProperty(Member member)
    {
        return Access.ReadOnlyPropertyThroughCamelCaseField(CamelCasePrefix.Underscore);
    }
}

public class Order
{
    private readonly ICollection<OrderRow> _rows = new Collection<OrderRow>();

    public virtual int Id { get; set; }

    public virtual DateTime Created { get; set; }

    public virtual ICollection<OrderRow> Rows
    {
        get { return _rows; }
    }
}

public class OrderRow
{
    public virtual int Id { get; set; }

    public virtual Order Order { get; set; }

    public virtual decimal Price { get; set; }
}

Это сгенерирует этот вывод:

NHibernate: INSERT INTO [Order] (Created) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 2011-04-29 13:40:39 [Type: DateTime (0)]
NHibernate: INSERT INTO [OrderRow] (Price, OrderId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 123 [Type: Decimal (0)], @p1 = 1 [Type: Int32 (0)]
NHibernate: INSERT INTO [OrderRow] (Price, OrderId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 456 [Type: Decimal (0)], @p1 = 1 [Type: Int32 (0)]
Saved
NHibernate: SELECT order0_.Id as Id0_0_, order0_.Created as Created0_0_ FROM [Order] order0_ WHERE order0_.Id=@p0;@p0 = 1 [Type: Int32 (0)]
NHibernate: SELECT rows0_.OrderId as OrderId1_, rows0_.Id as Id1_, rows0_.Id as Id1_0_, rows0_.Price as Price1_0_, rows0_.OrderId as OrderId1_0_ FROM [OrderRow] rows0_ WHERE rows0_.OrderId=@p0;@p0 = 1 [Type: Int32 (0)]
2
Fetched first time
NHibernate: SELECT rows0_.OrderId as OrderId1_, rows0_.Id as Id1_, rows0_.Id as Id1_0_, rows0_.Price as Price1_0_, rows0_.OrderId as OrderId1_0_ FROM [OrderRow] rows0_ WHERE rows0_.OrderId=@p0;@p0 = 1 [Type: Int32 (0)]
2

Во второй раз, когда я получаю ордер с использованием метода Get, он незапросить таблицу заказов, но она все равно выполняет запрос к таблице OrderRow.

Можно ли это как-то настроить так, чтобы она кэшировалась вместе с данными таблицы заказов?

1 Ответ

2 голосов
/ 29 апреля 2011

Кэширование коллекции обрабатывается отдельно от кэширования сущностей. Предполагая, что они автоматически преобразуются в .HasMany, вы можете определить для этого соглашение следующим образом:

public class CacheableCollectionConvention : IHasManyConvention, IHasManyConventionAcceptance {
    public void Apply (IOneToManyCollectionInstance instance) {
        instance.Cache.ReadWrite ();
    }

    public void Accept (IAcceptanceCriteria<IOneToManyCollectionInspector> criteria) {
        //whatever
    }
}
...