Showing posts with label Beginners guide. Show all posts
Showing posts with label Beginners guide. Show all posts

Friday, November 10, 2017

Caching partials in Umbraco 7.7

Umbraco has a nice way of implementing output caching in the views. It is exactly the same ass calling a partial, but you can say how long you want that partial to be cached and how should Umbraco understand if it should return cached data or build it again.
You could find the documentation in here.

Quick examples:

one can use CachedPartial without specifying anything else which means that you want the same partial showing every time no matter where it is.
For instance if you want to show footer or menu of your site, it is pretty easy to have them in a partial and then just call Html.CachedPartial("_menu",....) on the master page/ layout page.

  @Html.CachedPartial("_partial", Model, chachtime)//caching for everypage
On the other hand you might want to show different views for each member. An example could be a module to show username or other user specified data.
  @Html.CachedPartial("_partial", Model, chachtime,cacheByMember:true)//caching based on the member
Then there are times that you want to have your data showing differently based on different pages that they are on. For instance you want to write meta data of each page, but you don't care which user is that.
  @Html.CachedPartial("_partial", Model, chachtime,cacheByPage:true)//caching based on pages
 We can also do combination of page and members. 
  @Html.CachedPartial("_partial", Model, chachtime,cacheByPage:true,cacheByMember:true)//caching based on pages and members

But what if you want to cache your partial based on something else? What if you have some querystrings that contains some items that you want to differentiate based on that?
Lets say you have a small advertising site and for some reasons, you have your product Ids in the querystring. If you cache the page by page, it will just show everyone the first product that someone has browsed for the cache period which is not good.
Cachebymemebr is not also usable because you don't care if the person is logged in or different. You just care about the querystring that you have (productIds)
The way to do it is using contextualKeyBuilder like below: 

@Html.CachedPartial("_partial", Model, chachtime, 
contextualKeyBuilder:(o, dictionary) => 
CurrentPage.Id.ToString() + Request.QueryString.ToString())
You can specify the key based on your scenario or even pageview data.

Friday, October 20, 2017

Beginners Guide: Output caching in EPiServer



Introduction 


Output caching is not a new concept. There is a variety of project that people try to implement output caching, but how to do it in EpiServer?

What is Output caching?

The main purpose of using Output Caching is to dramatically improve the performance of an ASP.NET MVC Application. It enables us to cache the content returned by any controller method so that the same content does not need to be generated each time the same controller method is invoked. 

Why using output caching?

Output Caching has huge advantages, such as:
*Reduces the load on the server because there is no need to generate the results again
* Reduces the load on DB (same reason)
* Faster response

How it is done on MVC?

OutputCacheAttribute class has been implemented in System.Web.Mvc class and can easily being used simply by decorating the Controller with [OutputCache]. There are different variations for using it, which I am not going to discuss now, but you can read more in here.

What is the problem? Lets use it on EpiServer.

Although [OutputCache] attribute works fine for MVC applications, there are some functionalities that are specific to CMS systems. Let me elaborate more with an example:
- You have a page on your CMS and someone tries to view it for the first time. The MVC engine will try to generate the results and then save in in cache.
A second later, another person requests the same exact page. As you've guessed the MVC will receive the request and since it has it on its cache, it will respond with the same exact results.
- On the next step the editor change something on the page and publish it, but s(he) will see the same exact results as before since MVC doen't know about the change and will still return the same response. This also happens for the next people whom request the same page.

So to summarize we have 2 big problem:
1- content should not be cached for the editors
2- After publishing a page the old cache should be invalidate and the whole process should go through


How to fix it?

To fix the problems by yourself you need a lot of knowledge about EPiServer and you need some time to implement it. Luckily EPiServer has implemented their own ContentOutputCacheAttribute that will handle those for you :)

Simply add ContentOutputCache on top of your action 

 public class MyPageController : PageController<PageType>
 {       

   [ContentOutputCache(Duration = 3600, VaryByCustom = "*")]

   public ActionResult Index(StartPage currentPage)
    {
       ...
    }
}

Handle your GetVaryByCustomString in your Global.asax and you are good to go

public class EPiServerApplication : EPiServer.Global
{

 public override string GetVaryByCustomString(HttpContext context, string custom)
    {
       ....
    }

}
What to put into the GetVaryByCustomString depends on your setup, but one simple example could be returning the AbsoluteUri of the page. This way, different pages will differ, language versions will be handled automatically (since the AbsoluteUri will be different regardless of your routing config) but be aware that the personalization will not work since the AbsoluteUri is the same for all different users.

 public override string GetVaryByCustomString(HttpContext context, string custom)
    {
        return context.Request.Url.AbsoluteUri;
    }


****Very Important!!!!


ContentOutputCache will not work, unless you have the httpCacheExpiration in your web.config.
Simply go to your webconfig>
configuration>episerver>applicationSettings and make sure that it has properties for httpCacheability and httpCacheExpiration.

It should be something like this:

<configuration>
...
  <episerver>
    <applicationSettings httpCacheability="Public" httpCacheExpiration="0:10:00" .... />
  </episerver>
....
</configuration>



Thursday, July 14, 2016

EPiServer for beginers, where is version history

Version history is one of the basic features of any CMS and of course EPiServer has complete support over all of your pages and commerce data.
You might say that everyone knows that EPiServer has this feature, but to be honest I didn't expect to find it inside the gadgets pane the first time, so I thought why not mentioning it for beginners :)


So to see the version history follow these steps:
1-  In the editors page find the settings button on top right of your pane.


2- click on add gadgets

3- select "versions" gadget from the list


Version history is available for you. You can see who has done changes to the selected page and if the latest changes has been published or not :)

Monday, June 6, 2016

EpiServer for beginners: How to add custom fields to Order (eCommerce) programatically

Introduction

Some times ago, I had a post about adding a custom field to eCommerce from the user interface. That post got very popular and some folks like Khurram Khan and Steve C. mentioned that it is possible to add fields to eCommerce pragmatically. So in this post I will describe how to do it:

Steps
1- Create an initialization class

Create a class, inherit from IInitializableModule, add InitializableModule and ModuleDependency attribute to your class and you are almost there.

 [InitializableModule]

    [ModuleDependency(typeof(EPiServer.Commerce.Initialization.InitializationModule))]

    public class Initialization : IInitializableModule


* You will need to add "EPiServer.Commerce.Initialization" to have InitializationModule, "EPiServer.Framework" to have InitializableModuleAttribute and "Mediachase.MetaDataPlus.Configurator" to have MetaField methods but of course VS will add them for you when you write the names correctly


2-  Create the constructor and get the context
  public void Initialize(InitializationEngine context)

        {
        var mdContext = CatalogContext.MetaDataContext;

3- Use MetaField.Load to load your field from EPiServer.
        MetaField.Load(mdContext, name)


4- If the result was empty, create the field using MetaField.Create
The structure of the method is like this:

MetaField Create(MetaDataContext context, string metaNamespace, string name, string friendlyName, string description, MetaDataType dataType, int length, bool allowNulls, bool multiLanguageValue, bool allowSearch, bool isEncrypted);


* just to mention, length is the size of your data, so for example bool is 1, or DateTime is 8

5- Load the class that you want to add the field to it using  MetaClass.Load
  var mtClass = MetaClass.Load(mdContext, metaClassName);


6- Check if the meta field already exists in the meta class by checking its fields
  cls.MetaFields.Contains(field);


7- If the meta class doesn't have the class, add it using AddField
mtClass.AddField(field);


8- Smile :)

--------
It is very good idea to have simpler methods for adding fields and joining them to the meta class.

    private MetaField GetOrCreateMetaField(MetaDataContext mdContext, string metaDataNamespace, string name, MetaDataType type, int length, bool allowNulls, bool cultureSpecific)

        {

            var f = MetaField.Load(mdContext, name) ??

                    MetaField.Create(mdContext, metaDataNamespace, name, name, string.Empty, type, length, allowNulls, cultureSpecific, false, false);

            return f;

        }



        private void JoinField(MetaDataContext mdContext, MetaField field, string metaClassName)

        {

            var mtClass = MetaClass.Load(mdContext, metaClassName);



            if (MetaFieldIsNotConnected(field, mtClass ))

            {

                cls.AddField(field);

            }

        }



* And just to say: it is a good practice to have your strings inside enum classes. If you have a project, but you don't have any place for your enums and constants, you have to reconsider some of the stuff in your code :)

The whole code look like this:

 [InitializableModule]

    [ModuleDependency(typeof(EPiServer.Commerce.Initialization.InitializationModule))]

    public class Initialization : IInitializableModule

    {

  public void Initialize(InitializationEngine context)

        {



            MetaDataContext mdContext = CatalogContext.MetaDataContext;



            var myField = GetOrCreateMetaField(mdContext, Constants.Metadata.Namespace.Order,

          Extensions.PurchaseOrderExtensions.myFieldName, MetaDataType.DateTime, 8, true, false);

            JoinField(mdContext, myField, Constants.Metadata.OrderForm.ClassName);

        }



      private MetaField GetOrCreateMetaField(MetaDataContext mdContext, string metaDataNamespace, string name, MetaDataType type, int length, bool allowNulls, bool cultureSpecific)

        {

            var f = MetaField.Load(mdContext, name) ??

                    MetaField.Create(mdContext, metaDataNamespace, name, name, string.Empty, type, length, allowNulls, cultureSpecific, false, false);

            return f;

        }



        private void JoinField(MetaDataContext mdContext, MetaField field, string metaClassName)

        {

            var mtClass = MetaClass.Load(mdContext, metaClassName);



            if (MetaFieldIsNotConnected(field, mtClass ))

            {

                cls.AddField(field);

            }

        }



        private static bool MetaFieldIsNotConnected(MetaField field, MetaClass mtClass )

        {

            return mtClass != null && !cls.MetaFields.Contains(field);

        }

}

A Sample in a project
You can look at Steves  CommerceStarterKit and to be more specific, this page :) 

Acknowledgment
My gratitude to Steve Celius  for sharing his code with us.