2nd Level Caching - FindAll()

106 views
Skip to first unread message

jasonsirota

unread,
Feb 3, 2009, 7:15:16 PM2/3/09
to Castle Project Users
After wrestling with the trunk version of ActiveRecord and NHibernate
and NH-Contrib version numbers I finally got cache support enabled for
the AR project, however I'm having trouble getting it to work.

The call is something like:

Category.FindAll(new Order("SortOrder"),true)

which returns a list of category sorted by the db field "SortOrder".

I've enabled caching on the Category object like:

[ActiveRecord("Categories", Cache=CacheEnum.ReadOnly)]
public class Category
...

However, each time the page loads, it requeries the database instead
of pulling the resulting list from the cache.

SELECT this_.CategoryID as CategoryID4_0_, this_.Description as
Descript2_4_0_, this_.SortOrder as SortOrder4_0_, this_.Type as
Type4_0_ FROM BudgeterCategories this_ ORDER BY this_.SortOrder asc

Any ideas?
Jason

Stefan Sedich

unread,
Feb 3, 2009, 7:23:13 PM2/3/09
to castle-pro...@googlegroups.com
Do you not need to wrap your call to get all in a transaction? I
thought second level cache was only commited on commit of a
transaction?



Cheers
Stefan
--
Stefan Sedich
Software Developer
http://weblogs.asp.net/stefansedich

jasonsirota

unread,
Feb 3, 2009, 7:37:52 PM2/3/09
to Castle Project Users
I thought that you just had to wrap it in SessionScope, and
SessionScope is enabled for the full httprequest using the httpmodule

However, just to be safe, I wrapped it in a transaction:

Category[] defaultCategories;
using (new TransactionScope(OnDispose.Commit))
{
defaultCategories = Category.FindAll();
}

However, I still get the multiple db calls.

Stefan Sedich

unread,
Feb 3, 2009, 7:39:54 PM2/3/09
to castle-pro...@googlegroups.com
Ok no worries ignore me, I have never actually played with AR could be
something else entirely just remember reading about the importance of
use of explicit transactions when using 2nd level cache from the use
of NH Profiler.



Cheers
Stefan

jasonsirota

unread,
Feb 3, 2009, 7:41:17 PM2/3/09
to Castle Project Users
For reference, here's my config:

<activerecord isWeb="true" isDebug="false"
threadinfotype="Castle.ActiveRecord.Framework.Scopes.HybridWebThreadScopeInfo,
Castle.ActiveRecord">
<config>
<add key="connection.driver_class"
value="NHibernate.Driver.SqlClientDriver" />
<add key="dialect"
value="NHibernate.Dialect.MsSql2005Dialect" />
<add key="connection.provider"
value="NHibernate.Connection.DriverConnectionProvider" />
<add key="connection.connection_string" value="XXX"/>
<add key="proxyfactory.factory_class"
value="NHibernate.ByteCode.Castle.ProxyFactoryFactory,
NHibernate.ByteCode.Castle" />
<add key="cache.provider_class"
value="NHibernate.Caches.SysCache.SysCacheProvider,NHibernate.Caches.SysCache" /
>
<add key="relativeExpiration" value="300" />
</config>
</activerecord>

Ayende Rahien

unread,
Feb 3, 2009, 9:20:32 PM2/3/09
to castle-pro...@googlegroups.com
you need to enable query caching as well, and you need to mark the query as cachable.
I don't think you can do it using FindAll

Eric Hauser

unread,
Feb 3, 2009, 10:22:03 PM2/3/09
to Castle Project Users
Generally speaking, most things that apply to Hibernate apply to
NHibernate. First Google result turned up:

http://forum.hibernate.org/viewtopic.php?t=990508&view=previous&sid=0a058cf26ddfa235da88610b871b3389

Give that a shot and see if it caches.


On Feb 3, 9:20 pm, Ayende Rahien <aye...@ayende.com> wrote:
> you need to enable query caching as well, and you need to mark the query as
> cachable.
> I don't think you can do it using FindAll
>

Daniel Pupek

unread,
Feb 3, 2009, 11:09:19 PM2/3/09
to castle-pro...@googlegroups.com
Yes you must enable query caching and setcachable on your criterion queries. We have 2nd Level Caching working well. I also recommend giving all your objects and queries a cache region.


Dan

Checkout my blog @ http://blog.agilejedi.com
Checkout my homepage @ http://www.agilejedi.com

jasonsirota

unread,
Feb 4, 2009, 8:11:43 PM2/4/09
to Castle Project Users
So Ayende,

Why isn't FindAll() cacheable? It seems like any time you'd want to do
a FindAll(), you'd want them to be cacheable...shouldn't all the
activerecord generated queries be associated to a cache region, or are
they already?

Jason

On Feb 3, 6:20 pm, Ayende Rahien <aye...@ayende.com> wrote:
> you need to enable query caching as well, and you need to mark the query as
> cachable.
> I don't think you can do it using FindAll
>
Message has been deleted

jasonsirota

unread,
Feb 4, 2009, 10:38:05 PM2/4/09
to Castle Project Users
Okay I got this to work with this set of code:

ISessionFactoryHolder holder =
ActiveRecordMediator.GetSessionFactoryHolder();
ISession session = holder.CreateSession(typeof(Category));
ICriteria crit = session.CreateCriteria(typeof(Category));
crit.SetCacheable(true);
crit.AddOrder(new Order("SortOrder",true));
IList list = crit.List();
Category[] defaultCategories = new Category[list.Count];
list.CopyTo(defaultCategories, 0);

I feel like this is too many lines of code, is there some built in AR
functions. I feel like there should be something in AR
like:

Criteria crit = Category.CreateCriteria(); (a method of
ActiveRecordBase<Category>)
crit.SetCacheable(true);
crit.AddOrder(new Order("SortOrder",true));
Category.FindAll(crit);

or even better, overloads for find all

FindAll(bool cacheable)

Maybe I'm missing something....

Daniel Pupek

unread,
Feb 4, 2009, 11:03:35 PM2/4/09
to castle-pro...@googlegroups.com
Try this:

DetachedCriteria crit = 
DetachedCriteria.For<Category>()
                .SetCacheable(true)
                .AddOrder(new Order("SortOrder", true));
Category[] categories = ActiveRecordMediator<Category>.FindAll();

On a side not why are you trying to pull all categories? Just pull them as they are needed (using find by primary key)...they will be cached as they are pulled and subsequent calls will get the cached version.

Dan


Checkout my blog @ http://blog.agilejedi.com
Checkout my homepage @ http://www.agilejedi.com


jasonsirota

unread,
Feb 4, 2009, 11:07:00 PM2/4/09
to Castle Project Users
Interesting, that does seem easier. I thought there might be something
with DC but I couldn't figure out how to create one.

The reason I'm pulling all and caching is every page has a
"Categories" dropdown list that's needed before any dynamic items are
pulled from the database. So I could just pull the Name/ID pairs for
the dropdown list, populate it into a hash and cache it, but I know I
will need the category objects anyway and the overhead of pre-caching
the full list on application load is pretty low.

Jason

On Feb 4, 8:03 pm, Daniel Pupek <d...@agilejedi.com> wrote:
> Try this:
> DetachedCriteria crit =
> DetachedCriteria.For<Category>()
>                 .SetCacheable(true)
>                 .AddOrder(new Order("SortOrder", true));
> Category[] categories = ActiveRecordMediator<Category>.FindAll();
>
> On a side not why are you trying to pull all categories? Just pull them as
> they are needed (using find by primary key)...they will be cached as they
> are pulled and subsequent calls will get the cached version.
>
> Dan
>
> Checkout my blog @http://blog.agilejedi.com
> Checkout my homepage @http://www.agilejedi.com

Daniel Pupek

unread,
Feb 4, 2009, 11:20:44 PM2/4/09
to castle-pro...@googlegroups.com
You'll use the detached criterion a lot. Don't worry if the criterion syntax seems a bit odd. You'll pick it up fast. For SQL junkies HQL tends to go down a bit easier...I hear that hql exhibits a little better performance but we haven't noticed a difference.

A couple notes though, you can't resuse a DetachedCriteria once it has been passed to activerecord. You can call .Clone though to get a replica of it. Don't try to over optimize things on your own by using the session or static properties. You just end up stepping on top of what Nhibernate is doing for you. Nhibernate and active record have some interest ways to control the caching...you just have to play with it a bit and in the long run you'll be a lot happier.

Don't worry about opening and closing sessions....ActiveRecord does a fair bit of the session handling for you. You'll just end up creating a bunch of Stack Overflows. If you have a non-trivial situation and believe you need to circumvent the automatic session scopes then place your code inside of a using statement:

using(new SessionScope())
{

Do some active record stuff here!

}

ActiveRecord will generally try to use the last sessionscope opened...the using statement ensures it is disposed properly.

Dan


Checkout my blog @ http://blog.agilejedi.com
Checkout my homepage @ http://www.agilejedi.com

jasonsirota

unread,
Feb 4, 2009, 11:29:10 PM2/4/09
to Castle Project Users
Yes, it worked thank you for all your help, I ended up creating my own
static methods in inherited base classes so I can share the
functionality throughout the project, perhaps this isn't any cleaner,
but it makes me FEEL better :D

public static T[] FindAll(bool cacheable)
{
if (!cacheable)
return FindAll();
else
return FindAll(DetachedCriteria.For<T>()
.SetCacheable(cacheable));
}

public static T[] FindAll(Order order, bool cacheable)
{
return FindAll(DetachedCriteria.For<T>()
.SetCacheable(cacheable)
.AddOrder(order));
}

public static T[] FindAll(Order[] orders, bool cacheable)
{
return FindAll(DetachedCriteria.For<T>()
.SetCacheable(cacheable),orders);
}



On Feb 4, 8:20 pm, Daniel Pupek <d...@agilejedi.com> wrote:
> You'll use the detached criterion a lot. Don't worry if the criterion syntax
> seems a bit odd. You'll pick it up fast. For SQL junkies HQL tends to go
> down a bit easier...I hear that hql exhibits a little better performance but
> we haven't noticed a difference.
> A couple notes though, you can't resuse a DetachedCriteria once it has been
> passed to activerecord. You can call .Clone though to get a replica of it.
> Don't try to over optimize things on your own by using the session or static
> properties. You just end up stepping on top of what Nhibernate is doing for
> you. Nhibernate and active record have some interest ways to control the
> caching...you just have to play with it a bit and in the long run you'll be
> a lot happier.
>
> Don't worry about opening and closing sessions....ActiveRecord does a fair
> bit of the session handling for you. You'll just end up creating a bunch of
> Stack Overflows. If you have a non-trivial situation and believe you need to
> circumvent the automatic session scopes then place your code inside of a
> using statement:
>
> using(new SessionScope())
> {
>
> Do some active record stuff here!
>
> }
>
> ActiveRecord will generally try to use the last sessionscope opened...the
> using statement ensures it is disposed properly.
>
> Dan
>
> Checkout my blog @http://blog.agilejedi.com
> Checkout my homepage @http://www.agilejedi.com
>

Daniel Pupek

unread,
Feb 4, 2009, 11:36:26 PM2/4/09
to castle-pro...@googlegroups.com
Very sensible pattern. Good Job!

Dan


Checkout my blog @ http://blog.agilejedi.com
Checkout my homepage @ http://www.agilejedi.com

Markus Zywitza

unread,
Feb 5, 2009, 11:19:18 AM2/5/09
to castle-project-users
We accept patches, did you know ? ;-)

-Markus

2009/2/5 jasonsirota <jsi...@theknot.com>:

jasonsirota

unread,
Feb 5, 2009, 2:38:36 PM2/5/09
to Castle Project Users
yeah but then I have to do all sorts of "unit" testing, and "robust
fault-tolerant code", who has the time? ;-)

Jason

On Feb 5, 8:19 am, Markus Zywitza <markus.zywi...@gmail.com> wrote:
> We accept patches, did you know ? ;-)
>
> -Markus
>
> 2009/2/5 jasonsirota <jsir...@theknot.com>:

Tuna Toksoz

unread,
Feb 5, 2009, 2:46:30 PM2/5/09
to castle-pro...@googlegroups.com
ROTFL.



Tuna Toksöz
http://tunatoksoz.com
http://twitter.com/tehlike

Typos included to enhance the readers attention!
Reply all
Reply to author
Forward
0 new messages