Nov 17, 2013 at 8:34 PM
Edited Nov 17, 2013 at 9:22 PM
I thought about and experimented a bit with possible ways to implement shape-level caching and/or donut hole caching. The below is to publish the ideas for verification and some feedback (and also whether all this should possibly be part of Orchard.OutputCaching).
What do you think?
The two concepts below are either complementary or can live side by side.
Content shape output caching:
This is doable without modifying any existing code. The yield is not very big (would mostly help for certain LazyField computations) but works for authenticated users too, see the below points.
- It's fully possible out of the box (getting output from shape events, preventing display by modifying placement from handler).
- Top-level content shapes (e.g. Content itself) can be cached simply or even part shapes. Either way any any part-level lazy loading can be prevented (i.e. would prevent LazyFields from firing because of the parts' shapes' output being cached). Not sure
about whether part records would be loaded lazily without them being accessed because of shape output caching (but after The Shift this is less interesting).
- With part-level shape caching it could work even for authenticated users and also on editors. This is actually the main plus for implementing this.
- For the previous point there should be some way to configure which shapes to cache for authenticated and which ones for anonymous users (can be an admin config page for devs: e.g. cache Parts_Common_Body for authenticated users too).
- We could also have a part for enabling caching of shapes for a certain type, and configuring everything on the content type level.
- Would not prevent some basic queries from being run for retrieving items though.
This is just a very vague idea: unlike how it is done with Html.Action()s in standard MVC we could have it with shapes.
You can think about this a bit like generalizing the idea of driver shape factories to any shape: tie a factory to every shape to compute everything necessary for the shape. The output of the shape can be cached, then next time the factory won't run, the shape
won't be displayed but instead its place in the layout substituted with the cached content.
I'd imagine something like this to be returned from simple actions:
public ActionResult Index()
return new DeferredActionResult(() =>
var model = // Do some heavy work
Here the delegate will be only executed if there's no valid cache entry. Might not even need shapes for this.
For anything more complicated (like when an action's result contains pieces that can be computed individually) shapes would be used somehow like this:
public ActionResult Index()
return new DeferredShape() =>
var shape1 = new DeferredShape(() => compute everything for a shape and return the shape);
var shape2 = ...
return _shapefactory.WrappingShape(Shape1: shape1, Shape2: shape2);
Here if shape1, shape2 and WrappingShape wrapping both can be cached in any permutation.
The same "deferred shapes" would be used everywhere, making any piece of the layout individually cachable, together with their background computations. Downside is that any code creating shapes or views would have to be modified.
Many questions should be answered for an actual implementation (like how to identify such shapes) of course but this was only some high-level brainstorming to give an outline for the concept.
So what do you think?