Allow passing a Func instead of object in CachedPartial
See original GitHub issueThe CachedPartial extension is a great way to easily achieve partial caching. However, in our architecture, it has a great flaw. Calling the method requires passing in an already built model, and that is exactly where the possibly complex logic is that we want to cache!
Ideally, we pass a Func
that builds the model, that is only executed if the partial is not cached already.
We have created our own extension for that, but unfortunately it is quite hacky, as there is no way to check if a certain partial is already cached. Instead, what we do is pass first null
as model to CachedPartial
. This will throw an exception if there is no cache yet built, but will return successfully if there is already a cache. We then catch the exception, building the model and calling CachedPartial
again. The code is as follows (edited for brevity):
public static IHtmlString FuncCachedPartial(this HtmlHelper htmlHelper, string partialViewName, Expression<Func<object>> builder, int cachedSeconds = 3600)
{
IHtmlString result = null;
try
{
object model = null;
result = htmlHelper.CachedPartial(partialViewName, model, cachedSeconds);
}
catch (Exception ex)
{
bool isCachedException = (ex.InnerException is InvalidOperationException && ex.InnerException.Message.Contains("but this dictionary requires a model item of type"));
if (!isCachedException)
{
Diagnostics.Logging.Error(ex);
}
}
if (result == null)
{
object model = builder.Compile()();
result = htmlHelper.CachedPartial(partialViewName, model, cachedSeconds);
}
return result;
}
Issue Analytics
- State:
- Created 4 years ago
- Comments:5 (4 by maintainers)
Hi @rbottema! I’ve had a chat with Emma about this and although the PR for adding this in looks sensible, I’m a bit concerned about introducing this new logic:
@Html.Partial()
)?@Html.CachedPartial("~/Views/Partials/Home/Hero.cshtml", Model)
and place theHeroBuilder.Build(Model)
into the partial view) or even move this into a MVC action that’s called from the cached partial.@Html.CachedPartial()
method signature is very similar to the existing one, which might cause ambiguous method calls, especially when omitting the optional parameters.The last one is especially concerning, as the new overload might break existing calls to this extension method and thus existing sites that upgrade. So this would really need a good use case that’s currently not (easily) possible. Please do share this and/or give more insight into how/why, especially if I’ve missed something 😉
Hi @ronaldbarendse,
I’ll answer your points one by one:
() => HeroBuilder.Build(Model)
call to be ‘logic in the view’ as opposed to the controller? In that case I still disagree: The bad practice stems from separation of concerns and reusability, and the logic being in a separate class fulfills those goals.@model
of the partial then becomes the Umbraco model instead of our ViewModel. If we want to for example now build the same partial from a different page type, we either would have to create a whole new partial that is only different in the first line, have some indirection (i.e.: the partial only contains theBuild
method and loading the other partial) or have some kind of type switching happening, all of which I strongly don’t prefer over this solution.Func<object>
with anobject
, but if this is a large concern we can always rename the new overload.I hope this clears up some stuff and makes you as enthusiastic about this as I am =)