Skip to content
This repository has been archived by the owner on Sep 29, 2020. It is now read-only.

Cache by member #96

Open
bjarnef opened this issue Jun 28, 2018 · 7 comments
Open

Cache by member #96

bjarnef opened this issue Jun 28, 2018 · 7 comments

Comments

@bjarnef
Copy link
Contributor

bjarnef commented Jun 28, 2018

At the moment the caching of the LeBlender grid editors is by page, but it would be useful to cache by member as when using the CachedPartial e.g. when you present specific content inside the editor depending on the member type who is logged in.

IHtmlString CachedPartial(
    string partialViewName,
    object model,
    int cachedSeconds,
    bool cacheByPage = false,
    bool cacheByMember = false,
    ViewDataDictionary viewData = null,
    Func<object, ViewDataDictionary, string> contextualKeyBuilder = null)

https://24days.in/umbraco-cms/2017/the-one-with-performance/performance-boosts-for-umbraco/

https://our.umbraco.com/projects/backoffice-extensions/leblender/general-discussion/92538-caching-of-user-specific-content-in-leblender

@bjarnef
Copy link
Contributor Author

bjarnef commented Jun 29, 2018

@soreng do you have a suggestion how to solve cache by member for LeBlender grid editors?

@soreng
Copy link
Contributor

soreng commented Jun 29, 2018

@bjarnef I would suggest not caching the content for this.

Maybe you can use output caching in the page-controller, to get a similar result

@bjarnef
Copy link
Contributor Author

bjarnef commented Jun 29, 2018

@soreng I tried with [OutputCache] on the LeBlender controller Index method, but didn't seem to work when I rendered DateTime.Now.

public class CategoryProductListController : LeBlenderController
{
    [OutputCache(Duration = 3600, VaryByParam = "None", VaryByCustom = "User", Location = OutputCacheLocation.Server)]
    public ActionResult Index(LeBlenderModel model)
    {
        return View(model);
    }
}

However inside the grid editor I have some condition to render different actions from a SurfaceController, which returns another view e.g.

Html.RenderAction("GetProducts", "ProductList", new { ids = productPickerModel.SelectedProducts, pageSize = 12, mode = "carousel" });

When I use [OutputCache] on this action method DateTime.Now in this view is cached.

[OutputCache(Duration = 3600, VaryByParam = "None", VaryByCustom = "UserType")]
 public ActionResult GetProducts([ModelBinder(typeof(IntArrayModelBinder))] int[] ids, int page = 1, int pageSize = 10, SortBy sortBy = SortBy.None, SortDirection sortDirection = SortDirection.Ascending, string mode = "")
{
    int total = 0;
    var products = MyRepo.GetProducts(ids, page, pageSize, out total, false, sortBy, sortDirection);

    var pager = new PagerModel(pageSize, page - 1, total);
    var listModel = new ListModel(products, pager);

    ViewData["mode"] = mode;

    return View("/Views/Partials/ProductList.cshtml", listModel);
}

@soreng
Copy link
Contributor

soreng commented Jun 30, 2018

I dont see why it could’nt just use the method from core directly - but keep in mind LeBlender is build Against umbraco 7.4.

But back to the question. It requires the value of cacheByMember to be true somehow. My concern on this was that it could add alot stuff to memory if we are not carefull. It could be done via an attribute on the controller maybe?

@bjarnef
Copy link
Contributor Author

bjarnef commented Jul 2, 2018

@soreng it seems this extension class haven't changed much the last couple of years https://github.com/umbraco/Umbraco-CMS/commits/7ee510ed386495120666a78c61497f58ff05de8f/src/Umbraco.Web/HtmlHelperRenderExtensions.cs

When the core CachedPartial allowing caching by member and macros has this feature, would it make sense to allow this for LeBlender grid editors as well?

image

Furthermore the CachedPartial has a contextualKeyBuilder parameter, which can be used in e.g. a multi-site solution where same partials are used in each site, in this case it will be cached by domain.

@Html.CachedPartial("~/Views/Partials/MainMenu.cshtml", Model.Content, 1209600, contextualKeyBuilder: (model, viewData) => Request.Url.Host)

I have tried added the OutputCache on the LeBlender controller and the Index method, but it didn't seem to have any effect, but it does cache the partials when I add OutputCache on the action methods in my SurfaceController and these are rendered inside the LeBlender grid editor view, e.g.

[OutputCache(Duration = 3600, VaryByParam = "None", VaryByCustom = "Url;User")]
public ActionResult GetProducts([ModelBinder(typeof(IntArrayModelBinder))] int[] ids, int page = 1, int pageSize = 10, SortBy sortBy = SortBy.None, SortDirection sortDirection = SortDirection.Ascending)
{
	int total = 0;
	var products = ProductService.GetProducts(ids, page, pageSize, out total, false, sortBy, sortDirection);

	var pager = new PagerModel(pageSize, page - 1, total);
	var listModel = new ListModel(products, pager);

	return View("/Views/PartialView/ProductList.cshtml", listModel);
}
Html.RenderAction("GetProducts", "ProductList", new { ids = productIds, pageSize = 10 });

and then I have this.

public class Global : UmbracoApplication
{
    public override string GetVaryByCustomString(HttpContext context, string custom)
    {
        var args = custom.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
        var sb = new StringBuilder();

        foreach (var arg in args)
        {
            if (arg.InvariantEquals("Url"))
            {
                var filtered = HttpUtility.ParseQueryString(context.Request.QueryString.ToString());
                var absoluteUri = "url=" + context.Request.Url.AbsoluteUri;

                if (filtered["umbDebug"] != null)
                {
                    filtered.Remove("umbDebug");
                    absoluteUri = "url=" + context.Request.Url.AbsoluteUri.Split('?')[0];

                    if (filtered.Count > 0)
                    {
                        absoluteUri += "?" + filtered;
                    }
                }
                sb.Append(absoluteUri);
                //return absoluteUri;
            }
            else if (arg.InvariantEquals("User"))
            {
                if (context.Request.IsAuthenticated)
                {
                    var user = context.User.Identity.Name;
                    sb.Append("user=" + user);
                    //return "user=" + user;
                }
            }
            //else if (arg.InvariantEquals("UserType"))
            //{
            //    if (context.Request.IsAuthenticated)
            //    {
            //        var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
            //        var member = umbracoHelper.MembershipHelper.GetCurrentMember();
            //        if (member != null)
            //        {
            //            sb.Append("usergroup=" + member.ContentType.Alias);
            //            //return "usergroup=" + member.ContentType.Alias;
            //        }
            //    }
            //}
        }

        if (!string.IsNullOrEmpty(sb.ToString()))
        {
            return sb.ToString();
        }

        return base.GetVaryByCustomString(context, custom);
    }
}

I tried caching by member type, but it seems to create these SQL queries each time, so not sure how to cache an grid editor or partial by member group or member group?

@soreng
Copy link
Contributor

soreng commented Jul 11, 2018

Hi @bjarnef
The reason the LeBlender controller output is not affected by the output cache attribute is that the controller is not really used as a "real" controller. It is just there as a method for mapping from LeBlenderModel to your custom model, if needed.

For the caching to work, it should be implemented as you suggest - as it is in macros.

I'm getting ideas for improvements, but they may need a v2 to make them work.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants