Question

I've got this code:

[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; }
}

This will generate this output:

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

The second time I fetch the Order using the Get method, it doesn't query the Order table, but it still does the query to the OrderRow table.

Can this be configured some way so that it will be cached together with the Order-table data?

Was it helpful?

Solution

Collection caching is handled separately from entity caching. Assuming they are getting automapped as .HasMany, you can define a convention for this like so:

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

    public void Accept (IAcceptanceCriteria<IOneToManyCollectionInspector> criteria) {
        //whatever
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top