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>



Monday, September 11, 2017

Clean code trap for Entity framework




When writing a code, it is pretty normal to extract a part of your code to a new method in order to have a cleaner and human readable code.
Take this example:

PostTable (1) ===> (n) Votes


You have a post table that contains a lot of columns and you also like to retrieve total number of votes for that post.
A simple query would be:

db.posts.where(....).Select(Convert)
public static Dto Convert(Post p)
{
 return new Dto{
  Title= p.Title, 
  ...
  Other fields
  ....
  VoteCount= p.Votes.Count
 }
}

Simple. Right?
But then your query will be translated as a query to take Post object and 1 query for each of the posts to read its votes and it is not the worst part! it will fetch all the votes instead of counting them:
exec sp_executesql N'SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[PostId] AS [PostId], 
    FROM [dbo].[Votes] AS [Extent1]
    WHERE [Extent1].[PostId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=6

Then you might say, you can Include your vote table in your query. Like this:

 db.posts
.Include(p=>p.Votes).where(....).Select(Convert)
But then it will be translated into a big join

SELECT 
    [Project1].[Id] AS [Id], 
    [Project1].[Title] AS [Title], 
    [Project1].[C1] AS [C1], 
    [Project1].[Id1] AS [Id1], 
    [Project1].[Pid] AS [Pid], 
    [Project1].[PostId] AS [PostId], 
    FROM ( SELECT 
        [Extent1].[Id] AS [Id], 
        [Extent1].[Title] AS [Title], 
        [Extent2].[Id] AS [Id1], 
        [Extent2].[PostId] AS [PostId], 
        CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
        FROM  [dbo].[Posts] AS [Extent1]
        LEFT OUTER JOIN [dbo].[Votes] AS [Extent2] ON [Extent1].[Id] = [Extent2].[PostId]
    )  AS [Project1]
    ORDER BY [Project1].[Id] ASC, [Project1].[C1] ASC
So what is wrong?!!


As you may already have guessed it. Do not put the select into a separated method. call your query simply like this:

db.posts.Where(....).Select(p=>
new PostlDto{Title= p.Title, VoteCount= p.Votes.Count, ....})

Thursday, August 17, 2017

Forcing Umbraco install your DB again

Introduction


When you install Umbraco nuget package for the first time, it will show you a pretty installation windows and then tries to install the DB for you. What if you want to do the same when publishing your code somewhere else? What is you don't care about your site content and just want the views/ templates like for your test/staging server?



Umbraco settings


In the web config> <appSettings> of your project there is a setting that tells Umbraco which version are you on.
<add key="umbracoConfigurationStatus" value="7.6.5" />

If you remove it from the config, Umbraco has no other way than installing the DataBase again, and you will see the installation page when you try to access to the page. But wait! why doesn't it do anything?
If you take a look at the developer tools of your browser you will see that one of the requests has been failed



This is the  Error:

{"Message":"An error has occurred.","ExceptionMessage":"Invalid object name 'umbracoUser'.","ExceptionType":"System.Data.SqlClient.SqlException","StackTrace":"   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)\r\n   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)\r\n   at ....
So what is wrong?

Since we have set our connection string before, Umbraco thinks that there is a DB somewhere that has your User structure and tries to load your user. :)
Simply remove the Connection string and you will be fine 


Friday, March 3, 2017

Reactjs.net 3.0 and the error "Object doesn't support property or method 'assign' on server!" on ASP.net MVC (windows server 2012 r2)

Reactjs.net 3.0 and the error "Object doesn't support property or method 'assign' on server!"on ASP.net MVC 



* If you are too lazy to read the whole text just read the solution


Error

We are using Reactjs.net to generate some of our front-end code and it is a very good tool. On my new project, I've installed Reactjs.net v 3.0.1 on my local system and everything was fine until the time that I had to publish to test server which was Windows server 2012 r2 were I get the error

 
Failed to render mycomponent
Reason: Object doesn't support property or method 'assign'

Stacktrace
 

TypeError: Object doesn't support property or method 'assign' 

at o (Unknown script code:1:14914) 

at w._constructComponentWithoutOwner (Unknown script code:9:12601) 

at w._constructComponent (Unknown script code:9:12465) 

at w.mountComponent (Unknown script code:9:11635) 

at mountComponent (Unknown script code:1:13246) 

at Mixin.mountChildren (Unknown script code:10:9072) 

at h.Mixin._createContentMarkup (Unknown script code:9:25488) 

at h.Mixin.mountComponent (Unknown script code:9:24086) 

at mountComponent (Unknown script code:1:13246) 

at Mixin.mountChildren (Unknown script code:10:9072)


Digging in

So I started to dig in. I've published my code to my local IIS and test it with the same connection string and everything and it was fine, but when I copied the same files to the server I had the error.
With more investigation I found this post here on issues

By default ReactJS.NET will try to use V8, and fall back to the MSIE JS engine if V8 does not initialise correctly for some reason. There's a few machine-dependent bits:
  • V8 depends on the Visual C++ 2013 runtime (msvcp120.dll and msvcr120.dll)
  • MSIE depends on the version of Internet Explorer installed
You can disable the MSIE fallback by doing .SetAllowMsieEngine(false) in your config.


So as I've expected there is a strong relation between the error and the environment itself! Therefore, I've installed the latest version on Visual C++ 2013 runtime and checked Internet explorer version. but nothing worked.

Then I've found this post  were Andrey Taritsyn describes how to register Reactjs.net with Chakracore and test it on the server. but it still didn't work. After some more digging, I found out that even though JavaScriptEngineSwitcher.ChakraCore is in the packages, there is no dependency to Microsoft.ChakraCore  and therefore it cannot find the related dll for it :|.

Finding What is wrong

Reactjs.net uses System.Diagnostics.Trace to log the errors. Since you do not have access to console on test/ prod environment, you can just enable it to write to a log file:

To do so, go to web.config and add this:
 
<configuration>
<system.diagnostics>
<trace autoflush="true">
<listeners>
<add name="log" type="System.Diagnostics.TextWriterTraceListener" initializeData="output.log" />
</listeners>
</trace>
</system.diagnostics>
</configuration>

The solution

  1.  add Microsoft.ChakraCore nuget package
  2.  in your visual studio, go to App_start folder and create a class JsEngineSwitcherConfig

  3.  
     public class JsEngineSwitcherConfig
    {
    public static void Configure(JsEngineSwitcher engineSwitcher)
    {
    engineSwitcher.EngineFactories
    .AddChakraCore()
    //.AddJint() //only if needed
    //.AddJurassic()//only if needed
    .AddMsie(new MsieSettings
    {
    UseEcmaScript5Polyfill = true,
    UseJson2Library = true
    })
    .AddV8()
    ;
    engineSwitcher.DefaultEngineName = ChakraCoreJsEngine.EngineName;
    }
    }


  4.  Go to the Application_Start on Global.asax.cs and then add configure method to it. it should look like this:
  5.  
    protected void Application_Start()
    {
    .....
    
                RouteConfig.RegisterRoutes(RouteTable.Routes);
    
                JsEngineSwitcherConfig.Configure(JsEngineSwitcher.Instance);
    .......
    }


You are good to go 😀